公告栏-数据库优化

发布于 2024-10-05 06:39:21 字数 1435 浏览 3 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

杀手六號 2024-10-12 06:39:21

第一部分

于美国东部时间 10 月 10 日 12 月 9 日 01:00 修订

已查看您的 DDL。好的。我们需要退后一步,首先组织您的数据库。这将解决您一半的问题(您的 SQL 将直接、快速、索引更少、不需要临时表)。有一段时间我想,啊哈,你有你的专栏,它一定很稳定,但没有机会。从头到尾,好吧。看一下这个实体关系图(在我们得到正确的 ER 之前,在数据模型上工作是没有用的,即实体、关系和属性),并检查它是否正确。

  • 方法是回答以下问题(简短的回答即可)。这些问题澄清了实体和业务规则。如何理解一般数据库,特别是您的数据至关重要。您已经走了很长的路,靠自己的力量,所以我们可以从那里开始。

  • 我认为▶这篇文章◀ 可能对您有所帮助,以了解应遵循的正式阶段;我们在这里短路了。

  • 最重要的是,完全彻底地忘记功能和任何编码要求。数据必须独立于应用程序进行建模,就像数据一样。函数建模是一门不同的科学。首先做对;然后得到另一个权利;两人一起演奏优美的曲子。尝试将它们塞在一起;

为了简洁起见,也为了任何人阅读本文,我使用了封闭和开放部分;当一个开放项目(讨论)被关闭时,我会使其简洁,并将其移至“已关闭”部分。保持编号,因为有时事情会再次困扰我们。您可能希望这样做,甚至删除您这边的讨论。

漂亮图片的链接在最后。

抱歉:编辑不起作用;子编号不一致

已关闭问题

  1. users.bb_locations_csv 是用户和位置之间的多对多关系:
    • 每个元素都应该是离散列、离散行中的条目
    • 一个用户可以拥有多个位置并且一个位置可以拥有多个用户是多对多
    • 阅读▶这篇文章◀ 讨论如何处理以及处理到什么阶段
    • 在这个逻辑阶段,这只是一个::n关系,正如我所画的,你现在可以忘记它,简单地说,当我们到达物理阶段时,它就会被提供。
    • 相信我,我将提供不比 ...WHERE IN () 更复杂的代码来实现您声明的目的。
    • 转念一想,如果我折断了你的手指,你打字会更慢,所以我最好不要
    • 好的,您的应用程序是基于浏览器的,并且页面是动态的(我的建议是针对需要修改的静态页面);继续使用复选框。
      .
  2. users.bb_categories_csv 是用户和类别之间的多对多关系
    • 同上。
      .
  3. 已确认:没有用户,公告(bbs)就不存在;用户发布公告,整个周期就开始了;然后邀请回复和评分。

    3.1 已确认:实际上只有一个公告板,并且它在数据库中不作为事物存在。

    3.2 已确认:该组织永远不会有多个公告板,并且分类和类别都由类别表/函数充分处理

  4. 已删除。

  5. 已确认:公告和回复之间的区别在于,回复依赖于公告而存在,它们没有标题,并且不按位置或类别进行分类,因为它们依赖于公告本身而存在。

  6. 已删除。

  7. 已注意到评论。已解决。

7.1.对于另一用户提交的每个公告,每个用户可以发布多个回复。

7.2.对于用户提交的每个公告,该用户可以发布一个或多个回复。

7.3.已删除。

7.4.已删除。

数据模型现在允许每个用户在每个公告上有多个回复;包括提交公告的用户。

.
8.已确认:每个用户最多可以在公告上发布一个评分(可以撤销/更改)
.
9. 已确认:每个用户最多可以对一条回复发表一个评分(同上)

10.1.给定:用户名来自组织,是标识员工的唯一名称。例如,电子邮件是 [email protected] - 身份验证是使用 ldap 完成的需要连接并检索有关员工的其他信息

  • 确认:用户名是一个很好的标识符

10.2。已确认:名字、姓氏...出生地等保留为(传统)列,以确保人物不重复。
.
11. 鉴于:目前,我们可以通过组织内普遍熟知的临时名称来识别我们的办事处,因为我们只有大约 3 个主要办事处和许多外地办事处。例如华盛顿特区或弗吉尼亚州办事处。总的来说,我认为我们会尝试将总数保持在 20 以下。我还想记录每个位置的确切地址,因为这可以用于唯一地标识用户的办公室。

  • 提供:StateCode+Town 作为PK; IsMainOffice 作为布尔值。

.
12. 已确认:CategoryDescriptionName 为必填项。
.
13. 鉴于:用户将无法向某些类别发帖。只有具有足够高权限的用户才有权向某些类别发帖。

  • 提供:User, Location, Category 中的Permission 是评估此类权限的方法。

.
14. 已确认:Location.AdministratorLocation 管理员的UserId
.
15. 鉴于:永远只需要喜欢或不喜欢。我认为不需要保持中立立场,因为这与不投票是一样的?点赞似乎与诚实发布的公告回复更相关。也就是说,“我看到了你的回应,我不会写自己的回应,而是同意你的观点——现有的公告板在某种程度上是组织中的社会方面,我认为喜欢和不喜欢/同意和不同意会产生一定程度的争议,从而鼓励参与。然而,喜欢或不喜欢公告可能并不总是完全合适的。

15.1 提供:Like 作为 BulletinRatingResponseRating 中的布尔值。这将需要对每次访问进行解释。
15.2.当它不再是布尔值时,可以将其更改为RatingCode,并实现为查找表。然后,名称由连接确定,并消除解释。我在第一个数据模型中画了这个,这样你就可以明白我的意思
15.3。在第二个数据模型中删除。
.
16. 已确认:每个用户都有一个家庭位置(除了他们感兴趣的位置列表)。
.
17. 已确认:按照 (13) 的许可
.
18. 已确认:根据数据模型,可能需要进一步的权限。

18.1.如果您现在这样做,您就不必担心组织何时决定阻止某个发布响应公告,或者对他们进行评级;并希望昨天实现该功能。

18.2.即使您没有实施它,也要在您实施的价值观之间留下差距。
.
19 已确认:公告关于位置

19.1。已确认:没有位置公告

19.2。已确认:没有公告没有位置

19.3 已确认:没有用户(声明性)就没有公告。但到目前为止,我们还没有办法限制该User;因此,任何用户都可以为任何位置插入公告(您可以将其限制在代码中,例如位置每个用户都感兴趣

已确认:没有公告且评级为用户,则不存在BulletinRatings

19.4 已确认:没有公告就没有响应

19.4 已确认:没有响应响应就没有响应评级 。评级用户

。但是,可以有独立的用户、位置类别。

20. 如果您不介意,我将提供命名约定等。它们应该是不言自明的,并且只有当您开始编写 SQL 代码时,该值才会显示。如有不妥请追问。首先,所有名称都是单数。混合大小写更容易阅读(SQL 语言应该使用大写)。

20.1.我的经验是 table_name 相对于 tableName 来说是真正的技术形式,用户不喜欢它们;一致的混合大小写是每个人都喜欢的。这是无法改变的事情之一,因此请谨慎选择。
.
21. 对于您需要将表格分组在一起的需求(这很好),请记住这是一个物理问题。在逻辑数据模型级别,表具有正常的名称,不受物理问题的影响。想象一下,物理表的前缀类似于(请使用大写):
- REF_ 用于参考(例如用户)和查找表
- BUL_ 用于公告系统
.
我无法用大写字母命名表格?我不知道为什么。我不知道为什么我不能使用大写表名。与使用 MyIsam 数据库表有关吗?

通用约定是SQL语言用大写字母表示;我使用过的每个报告和管理工具都会生成这样的 SQL 代码。所以我们不能使用大写。仅小写或混合大小写。因此,选择归结为 table_name 或 TableName;我们需要某种分隔符。由于已经提供的原因,我强烈建议混合大小写,大写,而不是前导字母不大写的 OO 风格。

.
22. rank(全部)可以直接从数据库中导出(记住,在数据建模期间不要担心代码)。如果存储的话,就是Normalization error;重复的列;必须保持最新;这可能与派生值不同步;这称为更新异常。第五范式消除了更新异常。这是我的最低标准化水平,所以这就是你将从我这里得到的。

22.1。我根本不干涉排序顺序或受欢迎程度问题;事实上,听上去你还没有关闭该功能。我只取出冗余数据,即排名,作为标准化过程的一部分。

22.2.这是▶快速教程◀ 关于 RANK() 运算符(众所周知)。它不是 ANSI SQL;它是 Oracle 和 MS 的扩展。然而,如果您了解子查询,则不需要它,这就是 Sybase 没有它的原因。我怀疑 MySQL 有它,所以你需要了解一下它。了解标量子查询是先决条件。 Sybase 语法,因此请输入分号等。请随意提出具体问题。
.
我从未见过这种写 Rank = (SELECT...) 的方法与 Rank 的 (SELECT ...) 相同吗?

我已经为此发布了单独的答案。

.
22.3。需要了解为什么,完全没有问题。只有小孩子才会盲目地遵循简单的规则,而你肯定不是其中之一。
.
23、确认:users.total_bulletins是多余的;可以推导出来。已删除。
.
24.你所有的PK都是Id。您还没有厌倦在代码中迷失吗?忘记在所有移动的东西上粘贴Id物联网PK,让我们看看您的用户如何识别他们的实体;哪些实体是真正独立的,哪些实体依赖于独立实体。

24.1。切勿使用 Id 或任何此类形式。如果是 PK,请使用完整形式。

24.2.调用location_id,location_id,无论在哪里,包括PK表。例外是当你需要展示角色时。这将在数​​据模型中变得清晰。
.
25. 您没有声明性引用完整性,没有定义外键。出于多种不同的原因,这是个坏消息。一旦这些问题得到澄清,请添加它们。DRI 意味着尽可能多(如果不是全部)在 SQL 中声明完整性。 ISO/IEC/ANSI SQL 标准允许这样做,但市场的免费软件并未提供该标准,并且正在慢慢迎头赶上。这意味着服务器不允许在FK表中添加行,除非父表中存在PK。 MySQL 最近为外键提供了 DRI。对于FK,请参考▶这个文章◀

25.1。对于 CHECK 约束和规则,您必须在代码中实现它们。

我的外键就像,users-id(fk) = users.id(pk) 除了我所做的之外,我不知道如何添加它们,但一旦我知道如何添加,我肯定会这样做。

这不是将它们添加到您的数据库中;而是将它们添加到您的数据库中。这只是数据操作语言中的 WHERE 子句中的引用列,而不是数据定义语言。添加它们,以便它们在数据库/服务器级别运行,意味着按照链接的文章在 DDL 中声明它们。然后MySQL将阻止将一行插入到父PK不存在的子表(FK)中。这就是引用完整性。如果在DDL中声明,则为声明性引用完整性

除了执行RI之外,大家都可以看到定义:用户可以使用报告工具来访问数据库并从数据库中报告,而无需找人编写报告。

是的,据我所知。确认于▶此网站◀。我为子查询提供的代码使用 DRI,因此我们可以对其进行测试并尽早将其排除在外。您必须检查您的 MySQL 的特定版本。

二十五。 注释已注明。我不是 MySQL 专家。是的,这些都是你必须自己解决的问题。总的来说,从我的观察来看,MySQL 是没有腿的;对于任何 SQL 风格的东西,你都需要 InnoDB。

但不要让它阻碍你。现在使用 Engine=MySQL,不使用声明式 SQL,并继续使用数据模型和子查询。在后台处理 InnoDB。

要明确的是,我提供的 DDL 应该适用于 MyISAM(并且在 DRI 部门“不执行任何操作”,直到获得 InnoDB)。

.
27. 鉴于:我重新考虑了公告的排序要求。用户可以按时间顺序排序——简单、有意义。用户可以按照最新回复公告的日期对公告进行排序。然后我们就可以忘记排名,并且应该很容易按照公告的最后回复时间按时间顺序对公告进行排序?你有什么想法。

是的。这是明智且相当普遍的,大多数人都理解时间顺序。您将不得不弄乱他们在搜索窗口中选择的过滤器(选择:位置或列表;选择:类别或列表;选择:我的公告或全部)。


开放问题

(无)

数据模型

好的,假设您的 ERD 没有问题,并实现了所有已解决的问题,我已经对数据进行了建模,并准备了第五个数据模型(2010 年 12 月 9 日)供您审核。我确实需要关于此的更多反馈、问题等。我很难接受这件事已经完成了。也许最好开始为您的问题领域编写真正的代码。

链接

▶IDEF1X 符号链接◀ 在阅读数据模型之前,您确实需要阅读并理解这一点。

▶第五公告数据模型的链接◀实体关系图位于第一页,后面是数据模型

  • 按键几乎都是直接的IDEF1X(除了我作为对位提供的UserId);这意味着钱包关系密钥。未增强且未针对物理考虑进行优化。在你犹豫之前,首先注意它们、记录它们并评估它们。当然,我们可以添加 Id物联网密钥,但在此之前,让我们确保我们了解我们将丢失什么。

  • 注意符号文档中的标识符(实线)。脊柱,系统的椎骨是位置...公告...响应

  • 请注意,键实际上实现了许多业务规则。

  • 注意我渲染的自然层次结构。看看它对你是否有任何意义。

  • 动词短语非常重要;看看它们是否意味着什么。

关于第一个数据模型和响应的评论

我的一个问题是该位置的主键将用于形成子主键?(它们由实线连接)我不太明白这个概念< /em>

是的。 Location(在线上方)的 PK 为 (StateCode, Town)。无论如何,将两列一起 PK(复合键)作为 FK(粗体)从 Location 迁移到 Bulletin。我们还使用它来形成Bulletin PK(在线上方)。

如果我们需要代理键,我们将添加它。目前,我们正在制定标识符。所以需要思考的问题是:

  • 什么是好的公告标识符?,您的用户自然会使用什么来识别公告...
  • “您昨天看过弗吉尼亚州 FO 的公告吗?”,
  • “来自华盛顿的莎莉确实写了很好的公告”,等等。

或者为什么用户和公告之间不存在这种关系?

嗯,用户和公告之间不能存在那种关系,但是存在一种关系,虚线,表示 UserIdBulletin 中的 FK(粗体),但未使用它来形成其 PK(行下方)。

或者您的意思是:用户是 Bulletin 的强标识符(因此应该用于形成 BulletinPK,因此该行应该是实线)?

好吧。出色的。这就是标识符建模的全部内容。这清除了我不喜欢的一个领域,因为我们有非唯一的索引。这也解决了我的问题。

  • 根据上面进一步陈述的意图,由于我现在已将评级显示为表格以及渲染结果,一旦我将删除它

  • 我认为权限应该是一个实体。

  • 公告 PK 现在为(StateCode、Town、UserId、SequenceNo)。需要明确的是,SequenceNo 位于 StateCode、Town、UserId 内:对于 Sally 的有关 MO/Billngs FO 的第 5 个公告,它将为 5。

  • 注意,用户设置BulletinsPerPage等与User是1::1,所以它们在User中;子表将不正确。

  • 已更正印刷错误。

关于第二个数据模型和响应的评论

  • BulletinResponse 的 PK 已更改以反映 (7)。 BulletinNoResponseNo 已替换为 BulletinDateResponseDate(以前是 CreatedDate),以便允许每个用户每个公告进行多次回复。

关于第三个数据模型的评论和回复

相信您度过了愉快的时光。

  1. 至少在 30 年前(据我所知),行业巨头们曾进行过这样的争论。名字总是单数的。表是名词。动词短语是动词。这不仅限于数据库命名约定,它适用于文档、论文、学位论文等。您可能在文档末尾有 5 个结论,但在目录和页面顶部都有章节或章节标题是“结论”。

    在大学一路与他们抗争之后,当我开始第一份有偿编程工作,并看到规则在现实世界中的重要性,而不是我们在大学里的理论争论时,我就放弃了它浪费时间。我浪费的所有时间和精力都被释放出来去做富有成效的工作。从那时起,我不再质疑巨人;我只是接受。他们的思想比我的思想更伟大。这就像接受标准,或者在法律或上帝范围内行事。我没有真正非常充分的理由去做任何非法的事情。

    无论如何,这些规则所支持的语言(讨论、SQL、文档)的易用性无法得到充分的解释;当你编写越来越多的 SQL 代码时,它就会变得清晰。

    您始终可以自由地使用您想要的任何内容。我只提供单数。

  2. 对我来说很好。

    但是您需要记住,所识别的序列(例如非 PK 唯一索引或备用键)中的这两个元素是建立人员唯一性所普遍需要的。删除它们会导致两件事。首先,您将无法再识别用户之间的唯一性(因此您可能有重复的行)。其次,AK 变得不唯一,成为反转条目。

  3. 要点是(与其中一篇文章相反),任何与UserPK 为 1::1 的列都应位于User 中。所有首选项设置。由于我们清理了InterestedLocationsInterestedCategories,我只知道剩下的BulletinsPerPage;但是我敢肯定还有其他。 iSpReference2是EG。 boolean; numPreference3是一个EG。整数。等等。你可以告诉我真正的偏好是什么。

    (让我们在复数中尝试一下:... 用户 pk的任何1 :: 1的列,应驻留在用户中。只是不做对我来说,我挂在破碎的英语上,我对母语有些珍贵。)

    数据模型已更新。

  4. 很棒。让我知道您何时对此感到满意,我会给您物理模型。

    动词?

评论re 06 12月10日20:38 EST(小更新)


28。当然,PK仅出现PK,而FK列名称与PK列名称相同。但是,当FK中有多个OCC(看看响应)时,有三个userIds),我们需要区分它们。在IDEF1X术语中,这称为角色。发出Bulletin用户的角色是发行者,依此类推。显然,最好使用该名称,并在整个层次结构中保持一致(userId in Bulletin,然后当我们到达Response> Response> Response时,有两个,并且需要差异化,请将其更改为 。 >因此,绝对清楚的是用户ID作为FK,并且该角色是发行者; 同样,我们有许多DateTime列(如果您愿意的话,

请日期;否则DTM)。
.
29. IDEF1X符号文档是否没有意义?

  • 按指定顺序,每个表的PK位于行之上。
  • 请记住,无论如何我们都携带父表的PK,如果有意义,请使用这些FK形成孩子PK。
  • 对于Bulletin

    • 位置fk (statecode,town)发行的位置
    • 发行人的用户ID
    • 和DateTime是为了使其与众不同的。
    • 因此((司法,城镇,Issuerid,子弹)`
  • 删除此Bulletin的所有wenderseratings,在这四个<代码>公告列。

.
30。因为(状态,城镇)位置的PK,随身携带。它构成了Bulletin pk的一部分,因此任何因表都带有这些列,因为它们携带Bulletin pk。

寻找彩色选项卡(仅此版本)
.
32。这些是动词短语。符号文档中详细介绍了阅读它们的方法。看来您的手柄很好。正确获取表名(和动词短语)是正确的,因为实现后很难进行更改。如果您告诉我办公室比位置更好,那对我来说很好。

阅读:办公室被公告激活

随意提供另一个动词短语。
Aafaic,Office对ORG的其余部分都死了,并且仅在其雷达上生存(被激活)Bulletin
我在这里意识到这听起来很愚蠢,但是忽略了片刻,沿着“ Office的行为表达其功能;通过发行Bulletin”来宣传其活动。

在Mark的传感器数据模型上进行测验,用于一些不错的动词短语。

我们以前曾确定(州,城镇)是PK,我将按照将其保留为请参阅(38)以进行更改。

.
33。值得讨论。是的,如果要在(例如)显示响应时显示它,并且用户了解用户名。不,如果是30个字节,并且还有一个唯一的4个字节用户ID。当您最终确定约6列30字节键太麻烦了,无法迁移到孩子时,想法是有意识地意识到自己放弃的选择。

  • 我在一开始就确实说明了userId作为典型的id pk,因为它被携带/迁移到几个子表中。
  • 我们可以留下以后创建的方式。但这是一个纯粹的代理PK。

.
34。没问题。 类别已经拥有它。我将将订单更改为listorder

.
35。当然。根据我阅读和听到的内容,我对此感到非常满意。但是,在您编写代码之前,我希望更多来回获得一些信心。或者,将其视为一种学习体验,并接受模型和代码以后可能会发生变化。您要我现在生产身体吗?如果您给我所有更正,我将发布下一个版本。我希望用户中的首选项。另外,迅速浏览功能并检查您是否拥有所需的所有列。

为了学习和兴趣,请查看其他一些答案。

.
36.加入。您只需加入四个三列而不是一列。 SQL与Joins很麻烦,而应该使其更容易的新语法实际上更加麻烦。我的编码器从不写入加入:我们节省了时间和错别字。我有一个给定两个或多个表的PROC,将与所有列和连接在一起生成代码。我不了解MySQL,无法为您转换。

数据模型更新了。

评论RE RE 08 DEC 10 20:49,第四个数据模型和响应


检查上面的上一节,有小更新。

IDEF1X:您的速度很好。

请注意,孩子总是“继承”父pk,作为FK(固体或断线),否则它们之间没有关系。无论如何,通过使用这些孩子中存在的这些列来形成孩子PK,我们携带含义(这就是固体和破碎之间的区别)。因此,我们不需要为孩子寻找独立的标识符。当您进行编码时,此方法中的关系能力将在稍后变得清晰。

我们正在处理的部分是标识符:自然与非自然;有意义的与毫无意义。稍后,您将看到如何在由父pk形成子PK时使用引擎的关系能力。 (您的姓氏与父亲的姓氏不一样吗?)

了解关系数据库及其能力也很重要。当我们从OO的角度接近数据库(例如),并将其视为使我们的课程“持久”时,那将丢失。因此,我们将尝试学习和使用关系术语。当您去法国并期望他们会说美国人并使用相同的货币时,这会变得困难。学会说10个法语单词,他们张开双臂欢迎您,您与当地人会有很大的不同经历。

无论如何,继续实施模型。只是意识到我们可能会在某个时候进行更改。保存所有DDL。将所有测试数据保存为插入语句或表备份或字符格式导出(不知道MySQL在此领域无法做什么)。
.
37.1。处理,n :: n与Office&amp; 类别。当我们进入物理模型时,您只会“看到”。

37.2。完毕。

37.3完成。
.
38。太好了。也短。请注意,他们将永远无法在同一邮政编码中拥有两个办公室。数字(5,0)很好,但我认为美国正朝着7位数字迈进。没关系,您可以弄清楚。它是Office的绝佳PK。现在,本列是地址的一部分,可能是zipcode,已提高到更高的目的,而无需重复;由于我们将其携带在5个子表中,因此我们希望按照先前的惯例清楚pk名称,因此我们将其称为officeecode; OfficezipCode可能很愚蠢。

我们需要在name上进行唯一的索引,以确保它们不会添加两个具有相同名称的Office。注意,出于解释目的,这实际上是Office,替换(statecode,town)的逻辑键,并且仍然如此。

我仍然认为您可能需要statecodetown作为快速引用(除了坐在address> address中的某个地方,

数据模型已更新,现在可用于第五个审查。您没有说明您的喜好,... date vs ... dtm。我正在使用后者,因为它更加熟悉,也可以识别时间组成部分。易于改变。

这个答案已达到最大长度。继续在“第二部分”

Part I

Revised 09 Dec 10 01:00 EST

Looked at your DDL. Ok. We need to take a step back and organise your database first. That will solve half your problems (your SQL will be straight-forward; and fast; less indices; no temp tables required). For a while I thought, aha, you have your columns, it must be stable, but there is no chance. Top down from scratch, ok. Have a look at this Entity Relation Diagram (no use working on the Data Model, which is Entities, Relations and Attributes, until we get the ERs right), and check that it is correct.

  • The way to do that is, answer the following questions (short answers are fine). These questions are clarifying the Entities and Business Rules. How you understand databases in general, and your data in particular is crucial. You have come a long way, on your own, so we can take it from there.

  • I think ▶this post◀ might be helpful to you, in order to understand the formal stages that should be followed; which we are short-circuiting here.

  • Most important, totally, and completely, forget about the function and any coding requirements. Data has to be modelled independent of the application, simply as Data. Function Modelling is a different science. First get one right; then get the other right; and the two together play beautiful tunes. Try jamming them together; doing both tasks at the same time, and they won't even make a suburban garage band.

For brevity, and the sake of anyone reading this, I with use a Closed and Open Section; when an Open item (discussion) is closed, I will make it concise, and move it to the Closed section. Maintain the numbering, because things sometimes come back to haunt us. You may wish to do the same, or even delete the discussion on your side.

The links for the pretty pictures are at the end.

Apologies: the editing does not work; sub-numbering is inconsistent

Closed Issues

  1. users.bb_locations_csv is a many-to-many relation between users and locations:
    • Each of those elements should be an entry in a discrete column, in a discrete row
    • One users can have many locations and 1 location can have many users is many-to-many
    • Read ▶this post◀ for a discussion of how that is treated and what stage it is dealt with
    • At this Logical Stage, that is just a n::n relation, as I have drawn, you can forget about it for now, it will be supplied, simply, when we get to the physical Stage.
    • Trust me, I will provide code that in no more complex than ...WHERE IN () for your declared purpose.
    • On second thought, if I break your fingers, you will type even slower, so I better not
    • Ok, your app is browser based, and the page is dynamic (my advice was for static pages that need to be touched up); go ahead with check boxes.
      .
  2. users.bb_categories_csv is many-to-many relation between users and categories
    • Ditto.
      .
  3. Confirmed: a bulletin (bbs) does not exist without an user; an user issues a bulletin, and that starts the whole cycle; then invites replies and ratings.

    3.1 Confirmed: There is really only one bulletin board and it does not exist as a Thing in the database.

    3.2 Confirmed: that the org will never have more than one bulletin board, and the classifications and categorisations are all adequately handled by the Category table/function

  4. Deleted.

  5. Confirmed: The difference between bulletins and replies is that replies are dependent on a bulletin to exist, they do not have a title and they are not categorised by location or category because they are dependent on the bulletin itself to exist.

  6. Deleted.

  7. Comments noted. Resolved.

7.1. For each single bulletin submitted by another user, each user can post more than one reply.

7.2. For each single bulletin submitted by an user, that user can post one, or more than one reply.

7.3. Deleted.

7.4. Deleted.

The Data Model now allows more than one reply per user per bulletin; including the User who submitted the bulletin.

.
8. Confirmed: each user can post at most one rating to a bulletin (which can be revoked/changed)
.
9. Confirmed: each user can post at most one rating to a reply (ditto)

10.1. Given: username comes from the organisation and is the unique name that identifies employees. For example emails are [email protected] - authentication is done with ldap and this is required in order to connect an retrieve other information about the employees

  • Confirmed: UserName is an excellent Identifier

10.2. Confirmed: FirstName, LastName ... BirthPlace, etc remain as (the traditional) columns for ensuring People are not duplicated.
.
11. Given: At the moment we can Identify our offices by casual names which are generally know within the organisation, since we only have about 3 main offices and many field offices. So examples would be Washington DC or virginia field office. In total I think we will try and keep the total below 20. I want to record the exact address of each location as well because that could be used to uniquely identify offices to users.

  • Provided: StateCode+Town as PK; IsMainOffice as boolean.

.
12. Confirmed: Description and Name for Category are required.
.
13. Given:Users will not be able to post to some categories. Only users with sufficiently high rights will have the right to post to certain categories.

  • Provided: Permission in User, Location, Category is a method of evaluating such rights.

.
14. Confirmed: Location.Administrator is UserId of admin for the Location.
.
15. Given: There will only ever be a need for a like or a dislike. I don't think there needs to be a neutral position because this is the same as just not voting? Liking seems more relevant to bulletin replies that posts to be honest. Ie 'i see your response and instead of writing my own I will just agree with you - the existing bulletin board is somewhat of a social aspect in the orgainsation and I think liking and disliking/agreeing and dissagreeing creates a level of controversy that encourages participation. However liking or disliking a bulletin may not always be entirely appropriate.

15.1 Provided: Like as boolean in BulletinRating and ResponseRating. This will require interpretation on every access.
15.2. When it is no longer a boolean, it can be changed to a RatingCode, and implemented as a Lookup table. The names are then determined by Joins, and interpretation is eliminated. I drew this in the First Data Model, so that you could see what I meant
15.3. Removed in the Second Data Model.
.
16. Confirmed: each user has a home Location (other than the list of Locations that they are interested in).
.
17. Confirmed: Permission as per (13).
.
18. Confirmed: Further Permissions may be be required, as per Data Model.

18.1. If you do this now, you won't have to worry about when organisation decides to prevent a certain Person from posting Responses or Bulletins, or Rating them; and wants that feature implemented yesterday.

18.2. Even if you do not implement it, leave gaps between the values you do implement.
.
19 Confirmed: a Bulletin is about a Location.

19.1. Confirmed: There are no Bulletins without a Location

19.2. Confirmed: There are no Bulletins without a Location.

19.3 Confirmed: There are no Bulletins without a User (declarative). But so far we have no way of constraining that User; therefore any User can inset a Bulletin for any Location ( you could constrain it in code, eg. to Locations each User Is Interested In.

19.4 Confirmed: There are no BulletinRatings without a Bulletin and a rating User.

19.5 Confirmed: There are no Responses without a Bulletin.

19.4 Confirmed: There are no ResponseRatings without a Response and a rating User.

19.7. But, there can be Users, Locations, andCategories`, independently.

.
20. If you do not mind, I will provide naming conventions, etc. They should be self explanatory, and the value will show up only when you start coding SQL. Please ask, if anything isn't. For starters, all names are singular. Mixed Case is easier to read (you are supposed to use capitals for SQL language).

20.1. My experience is table_name as opposed to tableName are really technie forms, and users do not like them; Consistent mixed case is liked by everyone. It is one of those things that is impossible to change, so choose carefully.
.
21. For your need to group tables together, which is good, keep in mind that that is a Physical issue. At the Logical Data Model level, the tables have normal names, uncluttereded by physical issues. Imagine that the physical tables are prefixed with something like (and please use capitals for this):
- REF_ for reference (such as User) and lookup tables
- BUL_ for Bulletin system
.
I am not able to name tables with uppercase letters? Im not sure why. I don't know why I can't have uppercase table names. Is it to do with using MyIsam database tables?

The universal convention is that SQL Language is expressed in upper case; every report and admin tool I have ever used generates such SQL code. So we can't use upper case. Lower case or mixed case only. So the choices boil down to table_name or TableName; we need a separator of some kind. For reasons already provided, I strongly recommend mixed case, capiatlised, and not the OO style with the leading letter uncapitalised.

.
22. rank (all) can be derived directly from the database (remember, do not worry about the code during Data Modelling). If you store it, it is a Normalisation error; a duplicated column; which has to be kept up-to-date; which can get out of synch with the derived value; which is called an Update Anomaly. Fifth Normal Form eliminates Update Anomalies. That is my minimum level of Normalisation, so that is what you will get from me.

22.1. I am not interfering with the sort order or popularity issue at all; in fact, by the sounds of it, you haven't closed that functionality. I am only taking redundant data, the rank column, out, as part of the Normalisation process.

22.2. Here's a ▶Quick Tutorial◀ on the RANK() operator (as it is commonly known). It is not ANSI SQL; it is an Oracle and MS extension. However it is not required if you understand Subqueries, which is why Sybase does not have it. I doubt MySQL has it, so you need to get your head around it. Understanding Scalar Subqueries is a pre-requisite. Sybase syntax, so whack your semi-colons in, etc. Feel free to ask specific questions.
.
I have never seen that approach of writing Rank = (SELECT.... Is that the same as (SELECT ...) as Rank?

I have posted a separate Answer for that.

.
22.3. Needing to understand why, is no problem at all. Only children blindly follow simple rules, and you are certainly not one of them.
.
23. Confirmed: users.total_bulletins is redundant; it can be derived. Removed.
.
24. All your PKs are Ids. Haven't you gotten tired of getting lost in the code yet ? Forget about sticking Idiot PKs on everything that moves, let's find out How your users Identify their Entities; what Entities are truly Independent, and the other which depend on Independent Entities.

24.1. Never use Id or any such form. Where it is a PK, use the full form.

24.2. Call location_id, location_id, wherever it is, including the PK table. The exception is when you need to show the role. This will become clear in the Data Model.
.
25. You have no Declarative Referential Integrity, no Defined Foreign keys. That is bad news for many different reasons. Once these questions are clairified, please add them in. DRI means that as much as possible, if not all, Integrity is Declared in SQL. ISO/IEC/ANSI SQL standard allows for this, but the freeware end of the market does not provide the standard, and is slowly catching up. It means the server will not allow a row in the FK table to be added unless the PK exists in the parent table. MySQL recently provided DRI for Foreign Keys. For FKs, refer to ▶this article◀.

25.1. For CHECK constraints and RULES, you will have to implement those in code.

my foreign keys are like, users-id(fk) = users.id(pk) Im not sure how to add them other that what I have done but will certainly do so once I know how to.

That's not adding them into your db; that's merely referencing columns in a WHERE clause in Data Manipulation Language, not Data Definition Languge. Adding them, so that they function at the db/server level, means declaring them in DDL, as per the linked article. Then MySQL will stop a row from being inserted to a child table (FK) where the parent PK does not exist. That is Referential Integrity. If it is declared in DDL, it is Declarative Referential Integrity.

In addition to enforcement of RI, everyone can see the definition: report tools can be used by the users to access and report from the db, without having to get someone to code a report.

Yes, as far as I know. Confirmed at ▶this site◀. The code I have provided for the subquery uses DRI, so we can test that and get it out of the road early. You have to check for your specific version of MySQL.

Twenty-Five. Comments Noted. I ama not a MySQL specialist. Yes, those are the issues you have to figure out for yourself. In general, from my perusing, MySQL is legless; for anything SQL-ish, you need InnoDB.

But do not let that hold you back. Use Engine=MySQL for now, without the Declarative SQL, and keep going with both the Data Model and the Subquery. Work on InnoDB in the background.

To be clear, the DDL I have provided should work for MyISAM (and "do nothing" in the DRI department, until you get InnoDB).

.
27. Given: I have rethought the sorting requirements for bulletin. Users could sort chronologically- easy,makes sense. Users could sort bulletins by the date of the latest reply to the bulletin. Then we can forget about rank and it should be really easy to sort bulletins chronologially by the time of their last response? What are your thoughts.

Yes. that is sensible and quite common, most people understand chronological order. You will have to mess with the filters they choose in the search window (choose: Location or list; choose: Category or list; choose: My Bulletins or all).

Open Issues

(Nil)

Data Model

Ok, assuming you do not have issues with the ERD, and implementing all Closed Issues, I have modelled the data, and prepared a Fifth Data Model 09 Dec 10 for your review. I definitely need much more feedback, questions, etc, on this. I am experiencing difficulty accepting that it is done. Probably best to start writing real code for your problem areas.

Links

▶Link to IDEF1X Notation◀ You really need to read and understand this, before you read the Data Model.

▶Link to Fifth Bulletin Data Model◀ The Entity Relation Diagram is on the first page, followed by the Data Model.

  • The Keys are pretty much straight IDEF1X (except for UserId which I provided as a counterpoint); which means purse Relational Keys. Un-enhanced and not optimised for Physical considerations. Before you baulk at them, first notice them, register them, and evaluate them. Of course we can add Idiot keys, but before we do that, let's make sure we understand what we are going to lose.

  • Notice the Identifiers (solid lines) as per the Notation document. The spine, the vertebrae of the system is Location ... Bulletin ... Response.

  • Notice that Keys actually implement many Business Rules.

  • Notice the Natural Hierarchy that I have rendered. See if there is any meaning in it for you.

  • The VerbPhrases are really important; see if they mean anything.

Comments re First Data Model and Responses

One question I have is that the primary key of the location will be used to form the child primary key?(they are joined by a solid line) I don't really understand that concept

Yes. the PK for Location (above the line) is (StateCode, Town). That PK the two columns together, a compound key, is migrated from Location to Bulletin anyway, as an FK (bold). We are additionally using it to form the Bulletin PK (above the line).

If and when we need a Surrogate key, we will add it. For now, we are working out the Identifiers. So the question to contemplate is:

  • What is a good Identifier for Bulletin ?, what do your users naturally use to Identify a Bulletin ...
  • "have you seen the bulletin from Virginia FO yesterday ?",
  • "Sally from Washington sure writes good bulletins", etc.

or why that relationship does not exist between the user and the bulletin?

Well, that relation cannot exist between User and Bulletin, but a relation exists, the dotted line, meaning UserId is an FK in Bulletin(bold), but not used it to form its PK (below the line).

Or do you mean: the User is a strong Identifier for Bulletin (and therefore should be used to form the BulletinPK, therefore the line should be solid) ?

Fine. Excellent. That is what modelling re Identifiers is all about. That clears up an area that I did not like, in that we had non-unique indices. That resolves my issue as well.

  • As per intention stated further above, since I have now shown Rating as a table and what the rendering would be, once, I shall remove it

  • I think Permission should be an Entity.

  • Bulletin PK is now (StateCode, Town, UserId, SequenceNo). To be clear, SequenceNo is within StateCode, Town, UserId: it will be 5 for Sally's 5th bulletin re MO/Billngs FO.

  • Note that user Settings BulletinsPerPage,etc, are 1::1 with User, so they are in User; child table would be incorrect.

  • Typographical errors corrected.

Comments re Second Data Model and Responses

  • The PKs for both Bulletin and Response have been changed to reflect (7). BulletinNo and ResponseNo have been replaced with BulletinDate and ResponseDate (which used to be CreatedDate), in order to allow multiple replies per User per Bulletin.

Comments re Third Data Model and Responses

Trust you had a good break.

  1. At least 30 years ago (that I am aware of), the giants in the industry had this debate. Names are always singular. Tables are nouns. VerbPhrases are verbs. This is not limited to db naming conventions, it applies to documents, theses, dissertations, etc. You may have 5 conclusions at the end of the the doc, but the section or chapter title, in both the ToC and the top of the page is "Conclusion".

    After fighting them all the way through Uni, as soon as I started my first paid programming job, and saw the importance of the rules in the real world, as opposed to the theoretical arguments we had in college, I gave it up as a waste of time. All that time and energy I wasted was released to do productive work. Since then, I don't question the giants; I just accept. That their minds are greater than mine. It is like accepting Standards, or behaving within the law, or God. I have no really, really good reasons for doing anything illegal.

    Anyway, the ease of languaging (discussion, SQL, documentation) that is supported by such rules cannot be adequately explained; as you write more and more SQL code, it will become clear.

    You are always free to use whatever you want. I deliver singular only.

  2. Fine with me.

    But you need to keep in mind, those two elements, in the identified sequence (ala non-PK Unique Index, or Alternate Key) are universally required to establish Uniqueness for a Person. Removing them will result in two things. First, you will no longer be able to identify uniqueness across Users (and thus you may have duplicate rows). Second, the AK becomes non-unique, an Inversion Entry.

  3. The point is (contrary to one of the posts), any column that is 1::1 with theUserPK, should reside inUser. All preference settings. Since we cleaned up theInterestedLocationsandInterestedCategories, I know only of onlyBulletinsPerPageremaining; but I am sure there are others. IsPreference2is an eg. of a boolean;NumPreference3is an eg. of an Integer. Etc. You can tell me what the real Preferences are.

    (Let's try that in plural: ... any column that is 1::1 with theUsersPK, should reside inUsers. Just doesn't do it for me, I get hung up on the broken English, and I am a bit precious about my mother tongue.)

    Data Model Updated.

  4. Excellent. Let me know when you are comfortable with that, and I will give you the Physical Model.

    How about the VerbPhrases ?

Comments re 06 Dec 10 20:38 EST (Small Updates)

.
28. Where there is only one occurrence of PK as an FK, of course, the FK column name is the same as the PK column name. However, when there is more than one occ of the FK (take a look at ResponseRating), there are three UserIds), we need to differentiate them. In IDEF1X terminology this is called Roles. The Role of the User who issued the Bulletin is Issuer, and so on. Obviously it is better to use that name, and keep it consistent throughout the hierarchy (not UserId in Bulletin and then when we get to Response, where there are two, and a differentiation is demanded, change it to IssuerId. I thought you might have a problem with that; in the early stages, the usage is Issuer.UserId so that it is absolutely clear the it is UserId as an FK, and the Role is Issuer; when we get to the physical model, it gets simplified to IssuerId.

Likewise, we have many DateTime columns (Date for short if you like; otherwise Dtm), that need to be differentiated.
.
29. Did the IDEF1X Notation doc not make sense ?

  • The PK for each table is above the line, in the specified order.
  • Remember we are carrying the PKs of the parent tables anyway, and if there is meaning, using those FKs to form the child PK.
  • For Bulletin:

    • The Location FK (StateCode, Town) for which it is Issued
    • The UserId of the Issuer
    • and DateTime it was Issued, to make it unique.
    • therefore (StateCode, Town, IssuerId, BulletinDate)`
  • To delete all ResponseRatings for this Bulletin, use WHERE = on those four Bulletin columns.

.
30. Because (State, Town) is the PK of Location, carrying wherever. And it forms part of the Bulletin PK, so any dependent tables carry those columns because they are carrying the Bulletin PK.

Look for the coloured Tabs (This version only)
.
32. Those are Verb Phrases. The way to read them is detailed in the Notation doc. It appears you have a good handle on it. It is really important to get the table names (and the Verb Phrases) right, because change is difficult after implementation. If you tell me Office is better than Location, that's fine with me.

Read: Office Is Activated By Bulletin

Feel free to supply another Verb Phrase.
AFAIC, the Office is dead to the rest of the org, and only comes alive on their radar (is activated by) the issue of a Bulletin.
I realise it sounds silly here, but ignore that for a moment, something along the lines of "Office expresses its aliveness; advertises its activity, by issuing a Bulletin".

Have a quiz at Mark's Sensor Data Model, for some nice Verb Phrases.

We had previously identified that (State, Town) is the PK, I will leave that as is Refer to (38) for change.

.
33. Worth discussion. Yes, if you are going to display it when (eg) displaying Responses, and the users understand UserName. No, if it is 30 bytes, and there is also an unique 4 byte UserId. The idea is to make these choices consciously, aware of what you are giving up, when you eventually decide that some 6 column 30-byte key is too cumbersome to migrate to the children.

  • I did state at the outset, I would use UserId as a typical Id Pk, because it is carried/migrated to several child tables.
  • We can leave how that is created for later. But it is a pure Surrogate PK.

.
34. No problem. Category already has it. I'll change Order to ListOrder.

.
35. Sure. Based on what I have read and heard, I am quite happy with it. But I would like more back-and-forth to achieve some confidence, before you write code. Alternately, view it as a learning experience, and accept that the model and code may change later. Would you like me to produce the Physical now ? If you give me any and all corrections, I will publish the next version. I am expecting preferences in User. Also, quickly run through the functions and check that you have all the columns you need.

Do look at some of the other answers, for the purpose of learning, and interest.

.
36. Joins. You just join on four three columns as opposed to one. SQL is cumbersome with joins, and the new syntax which was supposed to make it easier, is actually more cumbersome. My coders never write joins: we save time and typos. I have a proc that given two or more tables, will generate the code with all the columns and joins. I don't know enough of MySQL to convert that for you.

Data Model Updated.
.

Comments re 08 Dec 10 20:49, Fourth Data Model and Responses

.
Check the previous section immediately above, there are small updates.

IDEF1X: Your speed is fine.

Note the child always "inherits" the Parent PK, as an FK (either solid or broken line), otherwise there is no Relation between them. By using these columns that exist in the child anyway, to form the child PK, we carry the meaning (and that is the difference between solid and broken). And thus we do not need to look for an independent Identifier for the child. The Relational power in this method will become clear later, when you are coding.

The section we are dealing with is about Identifiers: natural vs unatural; meaningful vs meaningless. Later you will see how we can use the Relational capability of the engine, when the child PK is formed from the parent PK. (Isn't your surname the same as your father's ?)

It is also important to understand Relational databases and their capability. That is lost when we approach the database (eg) from an OO perspective, and treat it as a location to make our classes "persistent". Therefore, we will try to learn and use Relational terms. It gets difficult when you go to France and expect that they speak American, and use the same currency; learn to speak 10 words of French, and they welcome you with open arms, and you'll have quite a different experience with the locals.

Anyway, go ahead with implementing the model. Just realise we will probably make a change at some point. Save all your DDL. Save all your test data as insert statements or as a table backup or character format export (no idea what MySQL can/cannot do in this area).
.
37.1. Handled, the n::n Relation with Office & Category. You will only "see" that when we get to the Physical Model.

37.2. Done.

37.3 Done.
.
38. Excellent. Shorter as well. Note they will never be able to have two Offices in the same Zip Code. NUMERIC(5,0) is good, but I thought the US was moving towards 7 digits. Doesn't matter, you can figure it out; it is an excellent PK for Office. Now this column, which was part of Address, probably ZipCode, has been elevated to a higher purpose, without duplication; since we are carrying it in 5 child tables, and we want the PK name to be clear, as per previously explained conventions, we will call it OfficeCode; OfficeZipCode might be silly.

We need an Unique Index on Name to ensure they do not add two Offices with the same name. Note, for explanation purposes, this is is actually the logical key of Office, replacing (StateCode, Town), and it remains so.

I still think you may need StateCode and Town as a quick reference (other than sitting somewhere in Address)

Data Model updated, Fifth now available for review. You did not state your preference, for ...Date vs ...Dtm. I am going with the latter, as it is more spceific, identifying the time component as well. Easy to change.

This Answer has reached maximum length. Continued in "Part II"

韶华倾负 2024-10-12 06:39:21

拥有高效数据库的关键是简化。关系数据库的主要目标是不重复任何信息。据我所知,我获取了您的 SQL 转储并快速起草了一个标准化的更简单版本。我确实为简历等留下了一些字段。我删除了一些字段,当需要信息时,通过查询数据库来重新计算会更简单,例如用户的帖子总数和给定帖子的排名。我还删除了您的 bb_replies,因为您可以通过引用父帖子来实现相同的结果。我已将表格稍微重命名为对我来说有意义的名称,您可以使用您觉得舒服的命名方案。我发现使用简单的术语可以更轻松地理解数据之间的关系。

我必须承认我确实同意上面的一些评论,有很多 BB 可以很好地工作并且具有您正在寻找的所有功能。你很幸运,我今晚有读书的心情,哈哈,这是一个很长的问题。简化是一切的关键:)

alt text

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';


-- -----------------------------------------------------
-- Table `users`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `users` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `username` VARCHAR(45) NULL ,
  `password` VARCHAR(100) NULL ,
  `email` VARCHAR(255) NULL ,
  `first_name` VARCHAR(100) NULL ,
  `last_name` VARCHAR(100) NULL ,
  `permission` INT NULL ,
  `created` DATETIME NULL ,
  `modified` DATETIME NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `categories`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `categories` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NULL ,
  `description` TEXT NULL ,
  `order` INT NULL ,
  `admin` INT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `locations`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `locations` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NULL ,
  `description` TEXT NULL ,
  `address` TEXT NULL ,
  `order` INT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `posts`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `posts` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `post_id` INT NOT NULL ,
  `user_id` INT NOT NULL ,
  `category_id` INT NOT NULL ,
  `location_id` INT NOT NULL ,
  `title` VARCHAR(45) NULL ,
  `content` TEXT NULL ,
  `created` DATETIME NULL ,
  `modified` DATETIME NULL ,
  PRIMARY KEY (`id`, `post_id`, `user_id`, `category_id`, `location_id`) ,
  INDEX `fk_posts_users` (`user_id` ASC) ,
  INDEX `fk_posts_posts1` (`post_id` ASC) ,
  INDEX `fk_posts_categories1` (`category_id` ASC) ,
  INDEX `fk_posts_locations1` (`location_id` ASC) ,
  CONSTRAINT `fk_posts_users`
    FOREIGN KEY (`user_id` )
    REFERENCES `users` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_posts_posts1`
    FOREIGN KEY (`post_id` )
    REFERENCES `posts` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_posts_categories1`
    FOREIGN KEY (`category_id` )
    REFERENCES `categories` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_posts_locations1`
    FOREIGN KEY (`location_id` )
    REFERENCES `locations` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `likes`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `likes` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `user_id` INT NOT NULL ,
  `post_id` INT NOT NULL ,
  `like` TINYINT(1)  NULL ,
  PRIMARY KEY (`id`, `user_id`, `post_id`) ,
  INDEX `fk_posts_users_users1` (`user_id` ASC) ,
  INDEX `fk_posts_users_posts1` (`post_id` ASC) ,
  CONSTRAINT `fk_posts_users_users1`
    FOREIGN KEY (`user_id` )
    REFERENCES `users` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_posts_users_posts1`
    FOREIGN KEY (`post_id` )
    REFERENCES `posts` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `sort_options`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `sort_options` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NULL ,
  `description` TEXT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `preferences`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `preferences` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `user_id` INT NOT NULL ,
  `pagination` INT NULL ,
  `sort_option_id` INT NOT NULL ,
  `categories_csv` VARCHAR(45) NULL ,
  `locations_csv` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`, `user_id`, `sort_option_id`) ,
  INDEX `fk_preferences_users1` (`user_id` ASC) ,
  INDEX `fk_preferences_sort_options1` (`sort_option_id` ASC) ,
  CONSTRAINT `fk_preferences_users1`
    FOREIGN KEY (`user_id` )
    REFERENCES `users` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_preferences_sort_options1`
    FOREIGN KEY (`sort_option_id` )
    REFERENCES `sort_options` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;



SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

They key to having an efficient database is to simplify. The main goal of a relational database is not to repeat any information. I took your SQL dump and quickly drafted a simpler version that is normalized, to the best of my knowledge. I did leave some of the fields you had in for cvs's ect. I have removed fields that it would be simpler to just recalculate by querying the db when the information is needed, such as a users total posts and a ranking of a given post. I also removed your bb_replies as you can accomplish the same result with referencing to a parent post. I have renamed the tables slightly to what made sense to me, you can use what ever naming scheme you feel comfortable with. I find that using terms that are simple makes it easier to understand how the data relates to each other.

I must admit that I do agree with some of the comments above, there are plenty of BBs out there that work just fine and would have all the functionality you are looking for. And you are lucky I am in the reading mood tonight lol that was one long question. Simplification is key in everything :)

alt text

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';


-- -----------------------------------------------------
-- Table `users`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `users` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `username` VARCHAR(45) NULL ,
  `password` VARCHAR(100) NULL ,
  `email` VARCHAR(255) NULL ,
  `first_name` VARCHAR(100) NULL ,
  `last_name` VARCHAR(100) NULL ,
  `permission` INT NULL ,
  `created` DATETIME NULL ,
  `modified` DATETIME NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `categories`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `categories` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NULL ,
  `description` TEXT NULL ,
  `order` INT NULL ,
  `admin` INT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `locations`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `locations` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NULL ,
  `description` TEXT NULL ,
  `address` TEXT NULL ,
  `order` INT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `posts`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `posts` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `post_id` INT NOT NULL ,
  `user_id` INT NOT NULL ,
  `category_id` INT NOT NULL ,
  `location_id` INT NOT NULL ,
  `title` VARCHAR(45) NULL ,
  `content` TEXT NULL ,
  `created` DATETIME NULL ,
  `modified` DATETIME NULL ,
  PRIMARY KEY (`id`, `post_id`, `user_id`, `category_id`, `location_id`) ,
  INDEX `fk_posts_users` (`user_id` ASC) ,
  INDEX `fk_posts_posts1` (`post_id` ASC) ,
  INDEX `fk_posts_categories1` (`category_id` ASC) ,
  INDEX `fk_posts_locations1` (`location_id` ASC) ,
  CONSTRAINT `fk_posts_users`
    FOREIGN KEY (`user_id` )
    REFERENCES `users` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_posts_posts1`
    FOREIGN KEY (`post_id` )
    REFERENCES `posts` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_posts_categories1`
    FOREIGN KEY (`category_id` )
    REFERENCES `categories` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_posts_locations1`
    FOREIGN KEY (`location_id` )
    REFERENCES `locations` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `likes`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `likes` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `user_id` INT NOT NULL ,
  `post_id` INT NOT NULL ,
  `like` TINYINT(1)  NULL ,
  PRIMARY KEY (`id`, `user_id`, `post_id`) ,
  INDEX `fk_posts_users_users1` (`user_id` ASC) ,
  INDEX `fk_posts_users_posts1` (`post_id` ASC) ,
  CONSTRAINT `fk_posts_users_users1`
    FOREIGN KEY (`user_id` )
    REFERENCES `users` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_posts_users_posts1`
    FOREIGN KEY (`post_id` )
    REFERENCES `posts` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `sort_options`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `sort_options` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NULL ,
  `description` TEXT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `preferences`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `preferences` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `user_id` INT NOT NULL ,
  `pagination` INT NULL ,
  `sort_option_id` INT NOT NULL ,
  `categories_csv` VARCHAR(45) NULL ,
  `locations_csv` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`, `user_id`, `sort_option_id`) ,
  INDEX `fk_preferences_users1` (`user_id` ASC) ,
  INDEX `fk_preferences_sort_options1` (`sort_option_id` ASC) ,
  CONSTRAINT `fk_preferences_users1`
    FOREIGN KEY (`user_id` )
    REFERENCES `users` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_preferences_sort_options1`
    FOREIGN KEY (`sort_option_id` )
    REFERENCES `sort_options` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;



SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
傲鸠 2024-10-12 06:39:21

首先是子查询,然后是 RANK() 函数

放轻松,儿子,我们会成功的!你的速度没问题。

准备

首先,您确实需要获得一套适合您的 MySQL 特定风格的手册。我找到了▶这个◀。和以前一样,您必须自己进行调试,但我现在提供尽可能接近通用 MySQL 的 SQL。我已经确认,我们要做的一切在那种风格的 MySQL 中都是完全可能的(我不知道你的风格/版本是什么,除了 ENGINE=MyISAM)。

子查询

好吧,让我们重新开始。我写了▶系列SELECTS◀ ,引导您完成整个过程。请完成每一项,并在进行下一项之前完全理解它。如果您有任何疑问,请停止并发布问题。

代码在Sybase中编写和测试;然后降级 MySQL(通过浏览网络,例如上述站点),并在该状态下进行尽可能多的测试。

第一个位创建并加载三个表以供使用。

  1. 第一个 SELECT 是三个表的直接连接,没有子查询。你需要让它发挥作用;也就是说,理解它的作用,修复任何语法问题;找出我提供的 SQL 和您服务器上运行的 SQL 之间的差异。并习惯于做出这些改变。我们不能一直为此停下来。

  2. 第二个 SELECT 产生完全相同的结果集。它引入了子查询的概念,用于填充单个列。

驾驶那辆公共汽车。完成后或遇到问题时进行回复。

评论的回复

  1. 对您2010年12月3日17:51直连
    我以前从未见过这种连接方式,我一直使用左连接、右连接或内连接。好的,对于第一个查询,我们只是将学生和课程这两个表与位于中间的学生课程表作为关联表连接起来。结果会按预期重复,因为一名学生可能上了一门以上的课程,并且他们将获得该课程的结果。

是的。

(WHERE子句中的x=y)是识别连接的传统方式,更清晰; LEFT/RIGHT/INNER/OUTER JOIN 语法是“新”方式。 AFAIC 要麻烦得多,但学习是相关的,因为它对以后的内容至关重要。为了便于理解,请随意转换为后一种语法,然后再转换回来。

重复?这不是重复或重复的意思。 CS 中的所有行都是离散的、真实的行。您应该在每个报告中获得相同的 15 行(随着我们的进展)。

(ps,当我使用您提供的查询直接创建表时,名称将转换为全部小写,而列名称仍然可以是驼峰式大小写。)

MySQL很奇怪。 (它似乎正在为我们进行命名约定!)


2. 简单标量查询
查询的一些问题。在定义别名之前,您使用别名(在标量子查询中)吗?(StudentCourse sc)我想我总是错误地认为您必须在使用别名之前先定义别名。

你正在按程序思考。 SQL 是一种面向集合的语言,用于操作关系数据

整个查询一次性得到评估和优化。没有“之前”或“之后”。我在使用它的同一批 SQL 中定义它。

我不完全理解标量子查询中别名“in-ner”的使用,这是否是说您希望它单独检查每一行(不知道如何解释这一点)而不是在表上宽检查?即,当您进行此检查时,使其位于您所在的特定行本地?(抱歉,解释很糟糕)。

为了理解/调试的目的,首先单独评估子查询(括号中的内容)。充分理解它。请注意“sc”的使用并将其保留在您的帽子中。

  • in_nersc 是别名,即 FROM 子句中紧邻的表名的句柄;为了方便起见,我们在代码中的其他地方使用
  • in_ner 是内部查询中引用的表的描述性名称,子查询
  • sc 是在内部查询中引用的表的描述性名称外部查询,它只是外部,因为它有一个内部查询,否则它将是一个平面查询,
  • 我们可以轻松使用 fredsally
  • 别名,例如 当内部查询和外部查询中引用同一个表时,in_nerout_er 才有意义。
  • 请注意内部查询和外部查询之间的连接 WHERE in_ner.CourseId = sc.CourseId
  • 我已关联in_ner查询中引用的表到 Out_er 查询中引用的表 sc
  • 这样的子查询称为相关子查询

看看您是否可以将外部查询(结果集)可视化为网格、电子表格、15 行 x 4 列。

  • 确保您理解外部查询,因为它确实“简单”。请注意,它与 (1. Straight Join) 相同,但填充一列的方法不同。

据我了解,标量子查询要求名称,其中 Course 和 Studentcourse 中的 courseId 相同。(非常简单),并且是在 where 中表示的另一种选择,

是的,完全正确。

请注意,我们只关注 Course.Name,它是在 CourseId 上从 StudentCourse 到 Course 的 1::1 连接。请注意,我们在 (2) 中替换了 (1) 中的 WHERE 子句; (1) 中它适用于所有行。

但是因为我们正在获取一个数据;一个细胞;特定行/列的一项;不是所有行;不是所有的列,它被称为标量。

我们使用子查询获取它,该子查询必须限制在特定行。因此,我们需要将外部查询中的行与内部查询中的行相关联。

  • 因此需要内部子查询和外部(特定行)之间的关联。

  • 如果我们没有特定行的标识,我们就会将垃圾加载到标量中,或者它将返回一个表(而不是标量值)并且查询将失败。

    • 尝试一下,将 WHERE CourseId = sc.CourseId 取出
    • 这样您就知道错误消息是什么,这样当将来发生这种情况时,您就会知道“啊哈,我返回的是一个表,而不是一个标量;我在我的内部WHERE”中遗漏了一些东西 子句;我没有识别特定的相关行”。

  • 它并不完全是“询问名称,其中课程和学生课程中的 courseId 相同”;它正在获取特定 StudentCourse.CourseId 的 Course.Name,该 ID 是从外部识别的,无论它是什么 sc 行。

不同的是,您可以在 where 之前逐行进行此检查。

你正在按程序思考;没有“逐行”; dbms 是面向集合的;您正在构建的结果是一个。重申问题是集合术语。

我使用了 Course 或者 in-ner,在这种情况下使用别名有什么意义,只是为了表明可以使用别名吗?

是的。并突出问题。并区分内部查询和外部查询。在内部查询中,不需要“内部”别名或任何别名。仅需要与外部查询相关的别名。

我不明白的是,当我尝试执行此操作时,“course.Name”在字段列表中显示未知的 Course.Name。这是我一直定义的方式,我的意思是课程表中的名称,而不是其他表中的名称。如果我有两个带有名称列的表会发生什么?

正是如此。如果不明确,那么您必须提供表名或别名;如果它不含糊,则不需要它,但为了记录、清晰、目的而有它是很好的。你必须弄清楚为什么 MySQL 不接受它。混合大小写/小写疯狂?

我也从未见过这种 order by 语法,我可以看到 1 和 4 表示列号,但为什么要费心传递两列呢?

嗯?因为我希望结果集按 Course.Name 升序排列,其中按 StudentCourse.Mark 降序排列。

如果我没有说明顺序,MySql 将以从 StudentCourse 获取的任何顺序生成结果集(按时间顺序?;按索引?)。无论默认顺序是什么,找出它,您需要知道它,从而在不必要时避免使用 ORDER BY

  • ORDER BY 拿出来玩玩。

  • 尝试 ORDER BY 4 DESC, 1

这不是“通过”,我是在一个 SQL 命令中告诉它如何处理我的结果集。您所做的唯一传递是在您的应用程序(PHP?)和 MySQL 之间。

2.1.好的,当您完成(2)并且非常高兴您理解它时,请做这个练习。

SELECT  (SELECT Name
            FROM  Course
            WHERE CourseId = sc.CourseId
            ) AS CourseName,
        () AS FirstName,
        () AS LastName,
        Mark
    FROM StudentCourse sc
    ORDER BY 1, 4 DESC

  • 生成相同的网格格式,我们想要与 (1) 和 (2) 完全相同的结果集。

  • 用适当的子查询填充两对空括号; IE。编写一个子查询来填充 FirstName 列,再编写一个子查询来填充 LastName 列

对您关于第三数据模型

2.1 的评论的响应。完美,是的,我们继续前进。
.
你正在用煤气做饭,所以如果你不介意的话,我会记下你的文字,并做一些注释;注意这些差异,它们可能很微妙,也可能不那么微妙。

相关标量子查询表示,对于每个课程 ID,我们需要最高分,而不是所有课程的最高分。这就是该子查询的相关方面发挥作用的地方,因为我们将外部查询与该特定行的内部查询相关联。 [是的!]我目前可视化的方式[就是这样,使用你头脑中的视觉部分,而不是连续部分]是外部查询运行将结果 table 集放在一起的表,并且每次创建一行时,它都会运行标量子查询并挑选出[一个值来填充单元格;这里是] courseId 匹配的最高分数,因此当它位于课程 id 为 66 的行时,标量子查询仅查找 courseId 为 66 的最大分数。

我自己说得更好。

不存在“结果表”这样的东西。

添加一项更明确的项目。

  • 外部查询定义结果集。

    • 子查询与此无关;它只是相关或索引的。

好的,那么 SQL 就可以工作了,对吗?

现在您已经了解了这一点,下一步是可视化结果集,并可视化填充整个列的子查询(3,未更改)。如果上面的文本是一个气球,一次填充一个单元格,那么想象数百个气球,填充连续的单元格。然后想象一个桶倒入柱中。

现在,将二维结果集单独放置一分钟,并可视化其顶部的另一层。这是并行层,您可以在其中编写子查询代码。

如果您在使子查询工作时遇到困难,请返回到您的可视化方式,一个结果集和子查询的另一层,该子查询倒入一桶标量以填充列。它消除了所有众所周知的子查询编码错误;删除了 GROUP BY、DISTINCT 的使用,以及所有那些让一条愤怒的长蛇装入果酱罐的笨方法。
.
在继续 (4) 之前,还要执行三个小步骤。

2.2 重新阅读我上面的回复(2),一直读到这里。没有略读。这是因为当你教给你的大脑一些新的、不同的东西时,你需要强化它。这是一种官方认可和标记的技术。

对 2010 年 12 月 8 日 20:49 评论的回复

2.3。在不使用子查询的情况下编写该查询 (3),并确保检查结果。如果您在编写代码时发现自己在笑,这是一个好兆头。只要生成正确的结果集,就可以通过,但请尝试编写最有效的代码(最少的 COUNTS 和 GROUP BY 等)。仅当您想在同事周围转圈,以便能够回答数据库中的任何“我如何编码......”问题时才执行此操作。

我不确定您所说的在不使用子查询的情况下编写该查询是什么意思?我认为我们想避免使用 group by 等

是的。绝对地。你已经向前走了。现在向后走不要绊倒。当使用子查询与连接更好时,这将真正帮助您理解向前行走。使用 GROUP BY 和 COUNT 对查询进行编码。最少的。别笑。

2.4.在您的数据库上编写子查询(3),以生成公告列表,外部查询必须仅为FROM bbs;包含喜欢计数和不喜欢计数。因此,截断表并执行 10 或 12 次有意义的插入,时间很长,很重要。

我使用了在数据库上使用子查询的方法来整理公告回复列表,计算喜欢和不喜欢的回复数量并获得特定的用户评级。这很棒,因为我不必使用任何分组依据或计数,也不必像为公告所做的那样创建临时表。

嗯,这很完美。现在我们在你的菠菜中获得了一些关系力量。

现在,去看看这个问题和答案;确保您比较代码。短短几天内,您已经取得了长足的进步。

完成 (2.3) 后,再次阅读 (2.4) 查询,以刷新自己,然后继续 (4)。

如果您遇到困难,请将“Rank”一词替换为“CountOfStudsWithHigherMark”,然后再试一次。

回复2010年12月11日13:14评论

2.3 在没有标量子查询的情况下编写该查询时遇到问题。甚至在我知道如何执行标量子查询之前,标量子查询对我来说总是更具有逻辑意义。这就是为什么我在上一个问题中说“我想我在这里遇到的问题是,如何在这个特定行中引用 user-id = x,而不是在所有行中”。答案是使用别名将标量子查询与主查询相关联。

(2.3) 练习的目的是让您:

  • 真正理解使用GROUP BY(在使用集合处理关系引擎的关系数据库中)与相关子查询的正确性、优雅性和速度。你已经做到了。这将使您在 SQL 编码能力方面优于同龄人。

  • 能够识别何时使用胖 WHERE 子句以及何时使用相关子查询是合适的。我不确定,但看起来您已经实现了这一点。

  • 在维护他人编写的代码时能够纠正和调试此类问题,并能够教他们区分。听起来你有很好的视觉、关系能力;通过练习得到了强化;现在你不能再回到劣等的方法了。也就是说,您可以理解并修复不正确的 SQL 代码,但无法将其传达给其他人。

只要您理解这些区别并接受这一点,我很乐意放弃(2.3)并继续前进。

再次阅读您的 (2.4) 查询,以刷新自己,然后继续 (4)。

如果您遇到困难,请将“Rank”一词替换为“NumStudentsWithHigherMark”,然后再试一次。


不要继续阅读。以下是“旧代码”

这里有一个▶快速教程◀ 在 RANK() 运算符上(众所周知)。它不是 ANSI SQL;它是 Oracle 和 MS 的扩展。然而,如果您了解子查询,则不需要它,这就是 Sybase 没有它的原因。我怀疑 MySQL 有它,所以你需要了解一下它。了解标量子查询是先决条件。 Sybase 语法,因此请输入分号等。请随意提出具体问题。

我从未见过这种写 Rank = (SELECT...) 的方法与 Rank 的 (SELECT ...) 相同吗?

是的,() AS Rank而不是Rank = ()都是合法的SQL; MySQL 可能不喜欢后一种形式。当然,括号包含子查询。请注意,Rank 是派生列的名称。

我已经说过,理解子查询是先决条件。这意味着数百万人之前就遇到过这个问题,讲师们发现,如果您按照规定的顺序学习课程,您会遭受更少的挫败感。所以暂时忘记 RANK,学习子查询。

试试这个(我提供 ANSI 标准 SQL;我没有 MySQL;你必须为 MySQL 进行语法修复;我不修复语法问题;那是你的工作):

< code>SELECT COUNT(*)+1 AS Id_iot -- 不是你,而是每个盲目使用它们的人
        (SELECT title in_ner FROM bb_locations WHERE out_er.bb_locations_id = in_ner.id) AS 位置,
        标题 AS 公告,
        创建日期 AS 日期
    来自bbs out_er

  • in_nerout_er 是别名,即 FROM 子句中紧邻的表名的句柄;为了方便起见,我们在代码中的其他地方使用
  • in_ner 是内部查询中引用的表的描述性名称,子查询
  • out_er 是在内部查询中引用的表的描述性名称外部查询,它只是外部,因为它有一个内部查询,否则它将是一个平面查询,
  • 我们可以轻松使用 fredsally
  • 连接
  • 注意我的 < strong>将in_ner查询中引用的表与out_er查询中引用的表相关
  • 这样的子查询称为相关子查询
  • 这只是一个例子,简单,这样你就可以学习子查询;特意选择提供与您熟悉的结果集相同的结果集,使用直接联接(FROM 子句中的 bbsbb_locations,联接通过 WHERE 子句或 JOIN 语法)。
  • 因为它产生单个值,所以被称为标量子查询(产生行的那些是表子查询;并且不能像这样使用,将单个值加载到每行中)
  • 没有建议任何人应该“使用子查询而不是加盟”。荒诞。子查询有其用武之地,连接也有其用武之地。米苏是另一回事。

现在,驾驶那辆公共汽车。并且,在您能够驾驶这辆公共汽车绕过数据库附近的每个角落而不杀死任何孩子之前,请不要跟我谈论 RANK。

我不明白内部和外部,当我用谷歌搜索它们时,我得到 INNER JOIN 他们叫什么,这样我就可以进一步研究

别名。请参阅上文。

当我运行该 select 语句时,我收到此错误 您的 SQL 语法中有错误; 检查与您的 MySQL 服务器版本相对应的手册,以了解在第 5 行的 'WHERE inner.Mark >=outer.Mark ) FROM Studentmark external ORDER B' 附近使用的正确语法

  • 首先,根据上面详述的原因, ,我不会写 MySQL 语法,调试是你的
  • 第二个工作,我意识到你无法调试你无法理解的东西,所以现在放弃它(它与 RANK 有关),当你学习 MySQL 风格时对于 SQL,所有这些问题都将得到解决,
  • 第三,让我向您保证它可以在任何标准 SQL 服务器上运行。它每年在大约 10 个课程中使用,因此每年有数百名参与者。我刚刚在 Sybase 上再次运行它,只是为了检查一下。
  • 我要建议的第一件事是,因为 MySQL 优化器很糟糕;它不理解上下文,innerouter 可能被视为保留字。所以按照上面的代码更改它。

Subquery First, then the RANK() Function

Relax, son, we'll get there! Your speed is fine.

Preparation

The first thing, you really need to get access to a decent set of manuals, for your specific flavour of MySQL. I found ▶this one◀. As before, you have to do your own debugging, but I am now providing SQL that is as close to generic MySQL as possible. I've confirmed that everything we are going to be doing is entirely possible in that flavour of MySQL (I don't know what flavour/version yours is, except ENGINE=MyISAM).

Subquery

Ok, let's start again. I have written a ▶series of SELECTS◀, to lead your through the process. Please complete each one, and understand it completely before progressing to the next. If you have any questions, stop, and post the question.

The code is written and tested in Sybase; then downgraded for MySQL (from perusing the web, eg. the above site), and tested as much as possible in that state.

The first bit creates and loads three tables for use.

  1. The first SELECT is a straight join of the three tables, no subquery. You need to get that to work; that is, understand what is does, fix any syntax problems; figure out the differences between the SQL I provide and the SQL runs on your server. And get used to making those changes. We can't keep stopping for that.

  2. The second SELECT produces exactly the same result set. It introduces the concept of a Subquery, which is used to populate a single column.

Drive that bus. Respond when you're done or if your have problems.

Responses to Your Comments of 03 Dec 10 17:51

  1. Straight Join
    I have never seen that way of doing joins before, I have always used left join, right join or inner join. Ok so for this first query we are just joining the two tables student and course with the studentcourse table sitting in the middle as the associative table. Results are repeated as expected because one student might be on more that one course and they will have a result for that course.

Yes.

That ( x=y in the WHERE clause ) is the traditional way of identifying joins, it is much more clear; the LEFT/RIGHT/INNER/OUTER JOIN syntax is the "new" way. Much more cumbersome AFAIC, but the learning is relevant because it is fundamental to what comes later. Feel free to convert to the latter syntax, and back again, for purposes of understanding.

Repeats ? That is not what repeats or duplicates mean. All the rows are discrete, true rows in CS. You should get the same 15 rows in every report (as we progress).

(ps when i direclty create the tables using queries you provided, the names are converted to all lowercase while the column names can still be camel case.)

MySQL is very strange. (It appears to be doing the naming conventions for us!)

.
2. Simple scalar query
A few issues with query. You use the alias(in the scalar subquery) before you have defined what it is?(StudentCourse sc) I guess I always incorrectly assumed that you have to say define an alias before you use it.

You are thinking procedurally. SQL is a set-oriented language, for manipulating Relational sets of data.

The whole query gets evaluated and optimised in one pass. There is no "before" or "after". I am defining it in the same batch of SQL that I am using it.

I don't entirely understand the use of the alias 'in-ner' in the scalar subquery, is this to say that you want it to check each row individually(not sure how to explain this) instead of on a table wide check?Ie when you are doing this check make it local to the particular row you are on?(terrible explanation sorry).

For purposes of understanding/debugging, evaluate the subquery first (the contents of the brackets), alone. Understand it fully. Note the use of "sc" and keep it in your hat.

  • in_ner and sc are ALIASES, that is, handles for the table name that it sits next to in the FROM clause; that we use elsewhere in the code for convenience
  • in_ner is a descriptive name for the table referenced in the Inner Query, the Subquery
  • sc is a descriptive name for the table referenced in the Outer Query, which is only Outer because it has an Inner query, otherwise it would be a flat query
  • we could just as easy use fred and sally
  • Aliases such as in_ner and out_er are meaningful when the same table is referenced in both the Inner and Outer queries.
  • notice the join between the Inner query and the Outer query WHERE in_ner.CourseId = sc.CourseId
  • I have related the table referenced in the in_nerquery to the table sc referenced in Out_er query
  • Such a subquery is called a Correlated Subquery

See if you can visualise the Outer query (result set) as a grid, a spreadsheet, 15 rows by 4 columns.

  • Make sure you understand that Outer query, "easy" as it is. Notice that it is the same as (1. Straight Join), with a different method of populating one column.

As i understand it the scalar subquery asks for Name where the courseId's in Course and studentcourse are the same.(pretty straight forward) and is an alternative to saying that in the where,

Yes, exactly.

And notice that we are after only the Course.Name which is a 1::1 join from StudentCourse to Course, on CourseId. Notice exactly the WHERE clause in (1) that we are replacing in (2); in (1) it applies to all rows.

But because we are grabbing one datum; one cell; one item for a specific row/column; not all rows; not all columns, it is called a Scalar.

We are obtaining it using a subquery, which has to be constrained to the specific row. Therefore we need to relate the row from the outer query to the row in the Inner query.

  • so the Correlation between the Inner Subquery and Outer (specific row) is required.

  • And if we did not have that identification of the specific row, we would be loading rubbish into the Scalar, or it would return a Table (not a Scalar value) and the query would fail.

    • Try that, take the WHERE CourseId = sc.CourseId out
    • So that you know what the error message is, so that when it happens in future, you will know "Aha, I am returning a table, not a scalar; I am missing something in my Inner WHERE clause; I am not identifying a specific Correlated row".
      .
  • it is not quite "asks for Name where the courseId's in Course and studentcourse are the same"; it is getting the Course.Name for a specific StudentCourse.CourseId, which is identified from the outside, whatever sc row it is.

with the differnece that you can make this check row by row before the where.

you are thinking procedurally; there is no "row-by-row"; the dbms is set-oriented; the result set you are building is a set. Re-state the question is set terminology.

I used Course instead or in-ner, what is the point of using an alias in this case, is it just to show that aliases can be used?

Yes. And to highlight issues. And to differentiate the Inner Query from the Outer query. In the Inner query, the "inner" Alias, or any alias is not demanded. Only the Alias relating to the outer query is demanded.

Something I don't understand here is that when I try to do this, 'course.Name' it says unknown Course.Name in field list. this is the way that I have always defined that i mean Name in the Course table and not some other table. What would happen if I had two tables with a name column?

Exactly. If it were ambiguous, then you would have to supply the table name or alias; where it is not ambiguous, it is not demanded, but nice to have for documentary, clarity, purposes. You have to figure out why MySQL is not accepting it. Mixed case/lower case madness ?

I have also never seen that order by syntax, I can see that 1 and 4 mean the column numbers but why bother passing it two columns?

Huh ? Because I want the result set ordered by Course.Name in ascending order, and within that, by StudentCourse.Mark in Descending order.

If I did not state the order, MySql would produce the result set in whatever order it gets it from StudentCourse (chronological ?; by index ?). Whatever that default order is, find that out, you need to know it, and thus avoid an ORDER BY, when it is unnecessary.

  • Take the ORDER BY out and play with it.

  • Try ORDER BY 4 DESC, 1

It is not "passing", I am telling it what to do with my result set, in the one SQL command. The only passing you are doing is between your app (PHP ?) and MySQL.

2.1. Ok, when you finished with (2), and completely happy that you understand it, do this exercise.

SELECT  (SELECT Name
            FROM  Course
            WHERE CourseId = sc.CourseId
            ) AS CourseName,
        () AS FirstName,
        () AS LastName,
        Mark
    FROM StudentCourse sc
    ORDER BY 1, 4 DESC

  • Produce the same grid format, we want the exact same result set as (1) and (2).

  • Fill in the two pairs of empty brackets with the appropriate subquery; ie. write a subquery to populate the FirstName column, and another to populate the LastName column

Responses to your Comments re Third Data Model

2.1. Perfect, yes, we move on.
.
You are cooking with gas, so if you don't mind, I will take your text, and annotate it a bit; notice the differences, they may or may not be subtle.

The correlated scalar subquery says that for each course id we need the highst mark, as opposed to the highest mark for all the courses. This is where the correlated aspect of this subquery comes into play because we are relating the outer query to the inner query for this particular row. [Yes!] The way that I am currently visualizing [That's it, use the visual part of your mind, not the serial part] it is that the outer query runs through the tables putting together the result table set, and each time it creates a row it runs the scalar subquery and picks out [a single value to fill the cell; here it is] the highest mark where the courseId's match, so when it is on a row where the course id is 66 then the scalar subquery is only looking for the max mark where the courseId is 66.

I could hardly have said it better myself.

There is no such thing as "result table".

Add one more definitive item.

  • The outer query defines the result set.

    • The subquery is independent of that; it is merely Correlated or Indexed.

Ok, so you have that SQL working, right ?

Now that you understand that, the next step is to visualise the result set, and to visulaise the subquery (3, unchanged) filling the entire column. if the above text was a balloon filling one cell at a time, then visualise hundreds of ballons, filling consecutive cells. Then visualise a bucket poured into the column.

Now leave that two dimensional result set alone for a minute, and visualise another layer on top of it. This is the parallel layer, where you write your subquery code.

If ever you have difficulty getting a subquery to work, go back to this, your way of visualising, one result set, and another layer for the subquery, which pours a bucket of scalars in, to fill the column. It eliminates all the well-known subquery coding bugs; removes the use of GROUP BY, DISTINCT, and all those ham-fisted methods of getting a long angry snake to fit into a jam jar.
.
Three more small steps before you proceed to (4).

2.2 Re-read my response (2) above, all the way down to this point. No skimming. This is because when you teach your mind something new and different, you need to re-inforce it. It is an officially recognised and labelled technique.

Responses to Comments of 08 Dec 10 20:49

2.3. Write that query (3) without using subqueries, and ensure you check the results. If you catch yourself laughing when you are writing the code, it is a good sign. As long as you produce the correct result set, you pass, but try to write the most efficient code (fewest COUNTS and GROUP BYs, etc). Do this only if you want to run circles around your peers, to be able to answer any "how do I code ..." question on your database.

I'm not sure what you mean by write that query without using sub-queries? I thought we wanted to avoid the use of group by's etc

Yes. Absolutely. You've walked forward. Now walk backward without tripping. This will really help your understanding of walking forwards, when it is better to use a subquery vs a join. Code the query with GROUP BYs and COUNTs. The fewest. Don't laugh.

2.4. Write the subquery (3) on your database, to produce a list of Bulletins, the outer query has to be FROM bbs only; with a count of likes, and a count of dislikes. So trunacte the tables and do 10 or 12 meaningful INSERTS, fibe minutes, big deal.

I used the method of using sub-queries on my database to put together a list of bulletins replies, count the number of reply likes and dislikes and get a particular users rating. it was great because I didn't have to use any group by's or counts and I didn't have to create temporary tables like I did for the bulletins.

Well, that's perfect. Now we are getting a bit of Relational Power in your spinach.

Now, go and look at this question and answer; ensure you compare the code. You've come a long way in just a few days.

When you finish (2.3), read your (2.4) query again, to refresh yourself, and move onto (4).

If you get stuck, replace the word "Rank" with "CountOfStudsWithHigherMark", and give it another go.

Responses to Comments of 11 Dec 10 13:14

2.3 I am having trouble writing that query without a scalar subquery. Scalar subqueries always made more logical sense to me even before I knew how to do them. That is why I said "I guess the problem I am running into here is, how do you refer to user-id = x in this particular row, not in all the row" in that previous question. Correlating the scalar subquery to the main query with and alias was the answer.

The (2.3) exercise is intended for you to:

  • really understand the incorrectness of the fat query with the GROUP BY (in a relational database using a set-processing relational engine) vs the correctness, elegance, and speed of the Correlated Subquery. You have achieved that. That will place you above your peers, in terms of SQL coding ability.

  • be able to identify when a fat WHERE clause and when a Correlated Subquery is appropriate. I am not sure, but it looks like you have achieved that.

  • be able to correct and debug this kind of issue when maintaining code written by others, and to be able to teach them the distinction. It sounds like you have a good visual, relational ability; which has been re-inforced by the exercise; and now you cannot go back to inferior methods. That is, you can understand and fix incorrect SQL code, but you cannot communicate that to others.

As long as you understand those distinctions and accept that, I am happy to drop (2.3) and move on.

Read your (2.4) query again, to refresh yourself, and move onto (4).

If you get stuck, replace the word "Rank" with "NumStudentsWithHigherMark", and give it another go.


Don't read further. The following is "old code"

Here's a ▶Quick Tutorial◀ on the RANK() operator (as it is commonly known). It is not ANSI SQL; it is an Oracle and MS extension. However it is not required if you understand Subqueries, which is why Sybase does not have it. I doubt MySQL has it, so you need to get your head around it. Understanding Scalar Subqueries is a pre-requisite. Sybase syntax, so whack your semi-colons in, etc. Feel free to ask specific questions.

I have never seen that approach of writing Rank = (SELECT.... Is that the same as (SELECT ...) as Rank?

Yes, () AS Rank instead of Rank = () are both legal SQL; MySQL may not like the latter form. The brackets containing the Subquery, of course. Note that Rank is the name of the derived column.

I have already stated that understanding subqueries is prerequisite. That means that millions before you have had this problem, and the lecturers figured out that you would suffer less frustration if you followed the lessons in the prescribed order. So forget RANK for now, and learn subqueries.

Try this (I supply ANSI Standard SQL; I do not have MySQL; you will have to syntax-fix it for MySQL; I don't fix syntax problems; that's your job):

SELECT  COUNT(*)+1 AS Id_iot  -- not you, everyone who uses them blindly
        (SELECT title in_ner FROM bb_locations WHERE out_er.bb_locations_id = in_ner.id) AS Location,
        title AS Bulletin,
        created_date AS Date
    FROM bbs out_er

  • in_ner and out_er are ALIASES, that is, handles for the table name that it sits next to in the FROM clause; that we use elsewhere in the code for convenience
  • in_ner is a descriptive name for the table referenced in the Inner Query, the Subquery
  • out_er is a descriptive name for the table referenced in the Outer Query, which is only Outer because it has an Inner query, otherwise it would be a flat query
  • we could just as easy use fred and sally
  • notice the join
  • I have related the table referenced in the in_nerquery to the table referenced in out_erquery
  • Such a subquery is called a Correlated Subquery
  • This is just an example, simple, so that you can learn Subqueries; purposely chosen to provide the same result set as one you are familiar with producing, using straight joins (bbs and bb_locations in the FROM clause, joining via the WHERE clause or JOIN syntax).
  • Because it produces a single value, it is called a Scalar Subquery (those that produce rows are Table Subqueries; and cannot be used like this, to load a single value into each row)
  • There is no suggestion that anyone should "use Subqueries instead of Joins". Absurd. Subqueries have their place, and Joins have theirs. Misue is a different thing.

Now, drive that bus. And don't talk to me about RANK until you can drive that bus around every corner in your database neighbourhood without killing any children.

I don't understand inner and outer, when I google them I get INNER JOIN what are they called so I can research further

Aliases. Refer above.

When I run that select statement I get this error You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE inner.Mark >= outer.Mark ) FROM studentmark outer ORDER B' at line 5

  • first, as per reasons detailed above, I can't write MySQL syntax, and debugging is your job
  • second, I realiise that you can't debug what you can't understand, so drop it for now (it has to do with RANK) and as you learn the MySQL flavour of SQL, all these things will be resolved
  • third, let me assure you that it runs on any Standard SQL server. It gets used in about 10 courses a year, so hundreds of participants per year. I just ran it again on Sybase, just to check.
  • first thing I would suggest is, since the MySQL optimiser sucks dead bears; it does not understand context, inner and outer are probably being treated as reserved words. So change that as per the above code.
娇妻 2024-10-12 06:39:21

第二部分

是第一部分的延续,因为该答案已达到最大长度。

2011 年 1 月 14 日 - 05:40 PST 评论修订

于 2010 年 12 月 11 日 13:14,第五数据模型和

响应IDEF1X 设计/绘图工具。
我不知道有任何免费软件选项。据报道,MySQL 设计工具经常崩溃。如果您对我的图表感到满意,我很乐意在这段时间内与您合作,直到最终模型得到解决; IE。我提供了数据模型,您可以跳过该任务。对于正在进行的工作,是的,您需要一个图表工具,也许不是数据库设计工具。请参阅 Notation 文档中 p2 末尾的我的评论。
.
29. 根据第五数据模型中的彩色选项卡,您清楚每个表中的 PK 和 FK 吗?我现在可以删除标签吗?
.
38. 关闭。
.
39. 所有Dtm 列都将是MySQL DATETIME 数据类型。您用于这些列的变量应该相同。 TimeStamp 有不同的含义。使用正确的数据类型是确保数据正确并且不允许非法值进入数据库的第一步(重要)。即,仅允许有效的日期和时间。此外,您可以从中查询任何日期或时间部分(例如月份或日期名称)。检查此文档。< br>
.
40.没问题。我们不只是硬编码一个类别(例如处理权限),而是实现一个 Category.IsRestricted ,然后 Permission 5 变为 Post Restricted公告
.
41. 完成。

您应该考虑对 CategoryUser 执行相同的操作。您希望能够通过设置指示器来删除它们,而不从数据库中删除条目(以及所有公告、响应、回复等)。它必须保留用于历史目的,但您需要禁止用户登录并执行任何操作。我已将其包含在 DM 中。

对于此类列名和布尔值,我个人更喜欢识别少数或异常情况,如 IsObsolete 中所示。

20.2.完毕。表和列命名现已发展为 InnoDB 格式。
.
子查询答案中的子查询响应。

评论 2010 年 12 月 13 日 13:14 美国东部时间和回应


41. 参见上文 (41) 和下一段。
.
42. 我的意思是: TitleDescription 就足够了;我们两者都不需要。
.
43.1。数据模型的实现。前进。这就是我昨天给你体检的原因。

.
43.2.设计/绘图。前进。我已经在上面(a)中评论过。

数据模型

第六种数据模型,包含上述所有更改。

物理意味着实现/编码所需的更多细节:数据类型; n::n 关系作为关联表实现;等等。您已经准备好实现数据模型,这意味着您需要物理模型。您已经弄清楚了关联表。因此,尽管您说您不着急,但我还是冒昧地向您提供了物理 DM。

  • 请注意,域(用户定义的数据类型)应始终在数据库中使用,两者都用于 DDL;您使用的 $variables。每个主键都有一个私有域。但这在 MySQL 中是不可能的,因此遗憾的是,数据类型是原始的。

  • 固定长度列比可变长度列快得多;我不提供(建议)Var 长度。您可以自由地实现您喜欢的内容。

  • 您确定需要 Category.Title Description 吗?我想不会,但我已将其保留在您确认之前。

  • 享受蓝色玻璃小按钮以及折叠实体中的导航。

请再次阅读IDEF1X Notation文档,我上周对其进行了扩展。

根据未决问题的关闭方式以及您可能遇到的任何问题,我们可以在第二天/晚上进行另一个版本。

评论 28 Dec 10 10:34 和回复

我已经开始实施数据模型。我假设第六个数据模型是物理模型,因为它包含关联表。

是的,我提供了它和数据类型,因为您说您已准备好实现。

仍有一些较小的未完成项目。也许是解决你的问题的好时机;我的所有三个答案,然后检查。例如,Category.TitleDescription

一旦完成,我将建立一个数据库转储。

这是没有必要的,因为模型已经定义了数据类型;但如果你确实发布了,我当然会帮你检查。电子邮件可能会更好。

然后我将列出我需要在数据库上运行的所有查询的列表并开始编写它们。

非常好的主意,采用结构化和有计划的方法来完成这项工作。

物理模型的实现

(39) 使用 mysql,我无法分配多个主键,因此我将按照您在文档中建议的那样使它们唯一且不为空。您认为对它们也建立索引是个好主意吗?

不确定您的意思,什么是“它们”?:

  • 一张表上永远不能有多个主键;备用键是唯一的,其中之一是“主”;在子表中作为 FK 携带。
  • 使用InnoDB(你所说的你会得到的),你可以使用MyISAM(你现在拥有的)定义主键约束(相当于UNIQUE,NOT NULL)
  • ,你需要一个索引,UNIQUE,NOT NULL作为主键( 对于 InnoDB 或 MyISAM,每个备用键(模型
  • 中的 AKx[.y])必须定义为附加索引,UNIQUE,NOT NULL。

评论 07 Jan 11 14:08 和回复

(40) 您能解释一下为什么category.CategoryCode 是一个 4 个字符的字符吗?为什么不像我们为用户那样使用数字?

40.1。这个想法是使用良好的自然标识符。数字对于用户来说毫无意义。如果我们没有大量的用户和用户流失,我也不会在那里使用数字。 CHAR(2) 或 (3) 或 (4) 允许他们为长 Category.Description 选择有意义的短代码,并且它足够小,可以作为 user_category 和 <代码>公告。

对于开发人员来说,在测试和调试时,公告列表中的短代码将非常方便。

(40) 我不太了解类别和位置的权限。假设我希望所有用户都能够向“旅行”类别发帖。我要把这个类别的权限设置为4吗?为什么我们在授予类别和位置权限时需要bool IsRestricted?

40.2.我没有改变Permission的概念或本质;这是你的想法,而且和你向我解释的一模一样。

(我所做的只是将permission实现为一个表。)

40.3。参见(13)和(40)原始交换。 category.IsRestricted 定义受限类别;有两类categories:受限制的和不受限制的。用户需要 权限 为 5 才能发布有关受限制类别的公告,并且需要拥有 4 权限才能发布有关不受限制类别的公告。

40.4。但是哇,儿子,你是在引入改变或扩展吗?例如。将userpermissioncategorypermission相匹配,从而允许多于两个类别的<代码>类别?请不要。这意味着权限对于类别意味着一件事,而对于系统的其余部分则意味着另一件事。或者,如果您这样做,那么我们必须首先解决确切的需求,然后将其作为更改实施。

40.5。该位 (40.2) 和 (40.3) 的位置(现在的办公室)完全相同。如果你指的是底部的文字,那是我的小错误,我会纠正它。

40.6。根据 (14),office 另外还有一个 AdministratorId (UserId)。在模型上显示为(允许的)用户

40.7。但这带来了一个问题:谁可以管理类别?现在任何人都拥有 5 或更高的权限,这是另一回事。我认为我们需要一些明确的东西,权限 6 = 管理类别。

其他

  • 已处理您的 DDL 并返回。

  • 数据模型已更新。修正了一些小的澄清和两个小错误。

评论 08 Jan 11 14:08 和回复

(我认为那是 1 月 9 日,而不是 1 月 8 日...我确实检查了更新。)

(40) 我不太明白类别和位置的权限。我没有改变任何东西。请忽略上一个问题的内容,因为请解释将设置什么权限以允许用户向“旅行”类别发帖。在我的实现中,我只有一个权限列。如果给定用户拥有所需的权限或更高权限,那么他们就可以向某个类别发帖,这就是新系统的工作原理吗?

是的。不变。 category.permission 与此无关。他们需要 user.permission 4 来实现不受限制的类别

如果category.IsRestricted,他们需要user.permission 5.

完全不同的是,用户需要任何类别的user.Permission。权限是为了管理类别。请勿使用小于 4 的值。

(41) 查询删除公告及其所有相关回复和评级。

我没想到,您确定他们不需要保留所有过去的公告作为历史记录吗?或审计目的?

无论如何,让我们在允许删除公告的基础上处理这个问题......

我什至不知道从哪里开始处理这个问题。过去,我加入的是将公告表与响应表、响应表与响应评级表以及公告 id = x 的公告评级表连接起来,然后将其删除。但现在任何一个特定的公告都由三列标识:OfficeCode、IssuerId 和 BulletinDtm。它们作为外键传递到子表。首先,我如何在我的 php 中存储要删除哪个公告?通常我会有一个像这样的链接 index.php?action=delete&bulletin-id=5。现在我必须有一个链接 index.php?action=delete&OfficeCode=20001&IssuerId=34&BulletinDtm=14:02... 我真的不知道如何做到这一点?

我无法帮助你,我是数据库和 SQL 专家,而不是 php 或 MyISAM 专家。您需要将其作为新问题发布在 SO 或 MySQL 板上。

b.就我对该主题的阅读而言,我不知道语法,但是是的,它可以完成,这是正常的。我在向您推荐复合键之前检查过。更正的 DDL 成功,并且索引已确认,对吗?

c.问题只是复合键或复合键所需的语法;并使用index.php。类似:

index.php?操作=删除& OfficeCode=x 和 IssuerId=y 和 BulletinDtm=z

d.为什么不能使用 mysql_query 而不是 index.php 并因此使用完整的 SQL ?据我了解,它与 MyISAM 一起使用。然后你可以使用:

`$sql = "DELETE $table WHERE OfficeCode=$OfficeCode AND IssuerId=$IssuerId AND BulletinDtm=$BulletinDtm";`

e.先删除response_ rating;然后响应;然后bulletin_ rating;然后是公告。当他们切换到 InnoDB 时,他们要做的更改就会减少。

f.最重要的是,您必须让他们确定删除公告的依据。任何及所有公告均不应删除。类似“一年没有活动”或“关闭”(这意味着添加了一个列)等。

评论 10 Jan 11 14:08 EST 和响应

(41.10-Jan-11) 没问题,方法很好,我有需要在上面 (41.*) 下解决的详细相关问题。 (41.f)还需要答案……除了权限之外,删除公告还有什么依据吗?

评论 10 Jan 11 13:48 pst 和回复

SO 编辑。别担心,那不是你。该网站的技术质量很差。编辑是无望的(相信我,我已经尝试使用它并围绕它进行工作,以使我的答案看起来有点就像我希望它们出现一样)。它无法正确处理缩进或多级编号。

删除基础。好吧,你有一个有效的基础。并且撰写回复的用户不会介意他们在未经询问的情况下被删除吗?

(41) 您正在寻找的是标准 SQL 中的“级联删除”,它是在外键子句中定义的(MyISAM 中没有该子句)。每个 INSERT/UPDATE/DELETE 动词仅适用于一个表,可能通过 REFERENCE 影响其他表。

对于非标准 SQL,您可以使用 DELETE multiple_table 方法(非标准语法)。

首先,理解这一点非常重要。 DELETE 命令中的 FROM 和 WHERE(或 JOIN)子句与 DELETE 本身是分开的;它们实际上是一个 SELECT。想法是:DELETE table_one (SELECT FROM table_one, table_two WHERE join_conditions)。

因此:

  • 在 DELETE(目标)中命名四个表
  • 在 FROM 中命名四个表(如何通过 SELECT 找到它们)
  • 确保您对这四个表具有正确(完整)的 JOIN 子句;您可以通过 SELECT 进行测试
    • 这意味着,JOIN ON OfficeCode、IssuerId、BulletinDtm(影响三个子表的 bulletin PK)
    • 使用 NATURAL 或 INNER 连接,使用左连接(作为一般规则,要明确,不要混淆它们)
  • 确保 WHERE 标识要删除的特定公告复合主键。

这是DELETE 语法JOIN 语法

2011 年 1 月 12 日 21:48 评论和

回复不要偷懒。自下而上编写四个删除语句。这就是我们必须在城市的尽头做的事情,在那里我们没有“级联删除”。为 rating_response 编写删除;然后复制粘贴,每次删除一行代码。我不明白这种焦虑或回避。

b.我再说一遍,不要使用左、右或任何类型的外连接(这仅是单个全包删除所需的)。仅使用直接内连接(这对于 4 个删除语句来说不是问题)。您遇到的任何烦恼都是由于您需要使用一次删除而导致的。放弃它,烦恼和并发症就会消失。

c.您没有忘记关系键的力量,几周前您就认识到了,不是吗。例如。能够从 rating_response 获取公告,而不必加入评级。如果你屈服于单列键的欲望,你将失去这一切。 SQL 很麻烦。但这就是我们所拥有的一切。处理它。非 SQL 试图“让生活变得轻松”,但事实上,引入了各种不必要的和可以避免的复杂性。举个例子。

2011 年 1 月 13 日 21:18 PST 的评论和回复

删除。三种口味。伟大的。希望您的数据值位于 $variables 中,这样就不会出现这种形式的重复。对于测试来说,这很好。

  1. 删除 x 四个表。 (不是“单独删除记录”,这是完全不同的事情;除了最后一个之外的每次删除都可能会删除数百行)。我相信剪切和粘贴只需要几秒钟。您需要小心,不要忘记更改表名称。

  2. 单个删除命令。第一个三元组的 $variables。您可以在除第一个三元组之外的所有列中使用列名称。

  3. 好的,测试后您将把 SELECT 转换为 DELETE。左连接。对于单个删除是必需的,但其他情况下则不需要。这与 (2) 相同,只是将 WHERE 替换为 JOIN>

我已经推荐了(1),但你更有可能选择(3)。

Part II

Continuation of Part I, due to that Answer reaching maximum length.

Revised 14 Jan 11 - 05:40 PST

Comments re 11 Dec 10 13:14, Fifth Data Model and Responses

a. IDEF1X Design/Diagramming tool.
I do not know of any freeware options. The MySQL design tool reportedly crashes often. If you are happy with my diagrams, I am happy to work with you for the duration, until the final model is resolved; ie. I provide the Data Model, and you can skip that task. For ongoing work, yes, you need a diagramming tool, perhaps not a database design tool. Refer my comments at the end of p2 in the Notation doc.
.
29. Are you clear about the PKs and FKs in each table, as per the coloured tabs in the Fifth Data Model; can I remove the tabs now ?
.
38. Closed.
.
39. All the Dtm columns will be MySQL DATETIMEdatatypes. The variables you use for those columns should be the same. TimeStamp has a different meaning. Using the correct Datatypes is the first (big) step towards ensuring that the data is coorect and no illegal values are allowed to enter the db. Ie, only valid dates and times will be allowed. Further, you can interrogate any date or time component (eg. month or day name) from it. Check this document.
.
40. No Problem. Instead of having just the one category hardcoded, how about (like the handling your of Permission), we implement an Category.IsRestricted and then Permission 5 becomes Post Restricted Bulletins.
.
41. Done.

You should think about doing the same for Category and User. You want to be able to delete them by setting the Indicator, without removing the entry (and all the Bulletins, Responses, Replies, etc) fro the database. It has to be retained for historical purposes, but you need to disallow the User from logging in and doing anything. I have included this in the DM.

For such column names and Booleans in general, personally I prefer to identify the minority or exception case, as in IsObsolete.

20.2. Done. Table and column naming now progressed to InnoDB format.
.
Subquery responses in Subquery Answer.

Comments 13 Dec 10 13:14 EST and Responses

.
41. See (41) and next para, above.
.
42. I meant: either Title or Description is enough; we do not need both.
.
43.1. Implementation of Data Model. Go ahead. That's why I gave you the Physical yesterday.
.
43.2. Design/Drawing. Go ahead. I have already commented in (a) above.

Data Model

Sixth Data Model supplied, containing all changes as per above.

The Physical means a lot more detail required for implementation/coding: Datatypes; n::n Relations implemented as Associative tables; etc. You are pretty much ready to implement the Data Model, which means you need the Physical. And you already have the Associative tables figured out. Therefore I have taken the liberty of providing you with the Physical DM, even though you said you were in no hurry.

  • Note that Domains (User Defined Datatyptes) should always be used in a database, both for the DDL; the $variables you use. And a private Domain for each Primary Key. But this is not possible in MySQL, therefore the Datatypes are raw, regrettably.

  • Fixed length columns are much faster than variable length; I do not provide (advise) Var length. You are free to implement what you like.

  • Are you sure you need both Category.Title and Description ? I think not, but I have left it in until you confirm.

  • Enjoy the little blue glass buttons, and the navigation from the Collapsed Entities.

Please read the IDEF1X Notation document again, I expanded it last week.

Depending on how the Open Issues close, and any issues you may have, we can progress another edition, in the next day/night.

Comments 28 Dec 10 10:34 and Response

I have begun implementing the data model. I assume that the 6th data model is the physical model because it contains the associative tables.

Yes, I supplied that, and the Datatypes, because you said you were ready to implement.

There are still a few minor outstanding items. May be a good time to go through your question; all three of my answers, and check. Category.Title and Description, for instance.

I will put up a database dump once I am done.

That is not necessary, given that the model has the Dataypes defined; but if you do post it, sure, I will check it for you. Email may be better.

I will then put up a list of all the queries that I need to run on the database and begin writing them.

Very good idea, to take a structured and planned approach to the job.

Implementation of Physical model

(39) With mysql I am not able to assign more than one primary key so I am just going to make them unique and not null as you suggest in the documentation. Do you think it would be a good idea to index them as well?

Not sure what you mean, what is "them" ?:

  • you can never have more than one Primary Key on a table; the Alternate Keys are Unique, one of them is "primary"; that is carried as FK in the child tables.
  • with InnoDB (what you said you will get), you can define PRIMARY KEY constraints (which is equivalent to UNIQUE, NOT NULL)
  • with MyISAM (what you have now), you need an Index, UNIQUE, NOT NULL for the Primary Key (above the line in the model)
  • for either InnoDB or MyISAM, each Alternate Key (AKx[.y] in the model) must be defined as an additional index, UNIQUE, NOT NULL.

Comments 07 Jan 11 14:08 and Response

(40) Could you explain why the category.CategoryCode is a char of 4 characters. Why not just use an number like we do for user?

40.1. The idea is to use good natural Identifiers. Numbers are meaningless to users. If we didn't have a large no of Users, and User churn, I would not have used a number there either. A CHAR(2) or (3) or (4) allows them to pick meaningful short code for the long Category.Description, and it is small enough to be carried as a Foreign Key in user_category and bulletin.

For the developer, when testing and debugging, that short code in a list of say bulletins, will be very handy.

(40) I don't quite understand permission for category and location. Lets say that I want all users to be able to post to the Travel category. Would I set the permission of this category at 4? Why do we need to bool IsRestricted when we are giving a permission to the category and location?

40.2. I have not changed the concept or essence of Permission; it was your idea, and it remains exactly as you explained it to me.

(All I did was implement permission as a table.)

40.3. Refer (13) and (40) original exchange. category.IsRestricted defines restricted categories; there are two categories of categories, Restricted and Unrestricted. The users need a permission of 5 to post bulletins re Restricted categories, and 4 to post bulletins re Unrestricted categories.

40.4. But whoa, son, are you introducing a change or extension; eg. match the permission of the user to the permission of the category, thus allowing far more than two categories of categories ? Please don't. That would mean permission means one thing re category and a different thing re the rest of the system. Or if you do, then we have to resolve the exact need first, then implement it as a change.

40.5. Location (now office) is exactly the same for that bit (40.2) and (40.3). If you are referring to the text at the bottom, it is my small mistake, will correct it.

40.6. As per (14), office additionally has a single AdministratorId (UserId). Shown on the model as (permitted) user.

40.7. But that brings up an issue: who can administer categories ? Right now anyone with permission 5 or greater, which is a different thing. I think we need something explicit, a permission 6 = Administer Category.

Other

  • Processed your DDL and returned.

  • Data Model Updated. Number of small clarifications and two minor errors corrected.

Comments 08 Jan 11 14:08 and Response

(I think that was 09 Jan not 08 Jan ... I did check for updates.)

(40) I don't quite understand permission for category and location. I haven't changed anything. Disregard the content of the previous question as explain what permission would be set to allow users to post to the 'Travel' Category. In my implementation I simply had a permission column. If a given user had the required permission or greater then they could post to a category, is that how the new system works?

Yes. Unchanged. category.permission has nothing to do with it. They need user.permission 4 for unrestricted categories.

If the category.IsRestricted, they need user.permission 5.

Quite separately, an user needs user.Permission of whatever category.Permission is, in order to administer category. Do not use values less than 4.

(41) Query Delete a bulletin and all its associated replies and ratings.

I did not expect that, are you sure they have no need to keep all past bulletins for historical or audit purposes ?

Anyway, let's deal with that on the basis that deleting bulletins is allowed ...

I don't even know where to get started on this. In the past I have joined would have joined up the bulletin table with the response table and the response table with the response rating table and the bulletin-rating table where the bulletin id = x and deleted them. But now any one particular bulletin is identified by three columns: the OfficeCode,IssuerId and BulletinDtm. Which are carried to the child tables as foreign keys. For a start, how do I store indicate which bulletin is to be deleted in my php? Usually I would have a link like this index.php?action=delete&bulletin-id=5. Now will I have to have a link that is index.php?action=delete&OfficeCode=20001&IssuerId=34&BulletinDtm=14:02... I really have no clue how to do this?

a. I can't help you there, I am a database and SQL expert, not a php or MyISAM expert. You will need to post that as a new question on SO or the MySQL boards.

b. As far as my reading of that subject has taken me, I don't know the syntax, but yes, it can be done, it is normal. I checked before recommending composite keys to you. The corrected DDL succeeded, and the indices are confirmed, correct ?

c. The issue is simply the syntax required for composite or compound keys; and working with index.php. Something like:

index.php? action=delete & OfficeCode=x and IssuerId=y and BulletinDtm=z

d. Why can't you use mysql_query instead of index.php and thus use full SQL ? As I understand it, it works with MyISAM. Then you can use:

`$sql = "DELETE $table WHERE OfficeCode=$OfficeCode AND IssuerId=$IssuerId AND BulletinDtm=$BulletinDtm";`

e. Delete response_rating first; then response; then bulletin_rating; then bulletin. When they switch to InnoDB they will have less changes to make.

f. Most important, you will have to get them to identify the basis on which a bulletin can be deleted. ANy and all bulletins should not be deleted. Something like "no activity for one year" or "closed" (which means an added column), etc.

Comments 10 Jan 11 14:08 EST and Response

(41.10-Jan-11) No problem, the method is fine, and I have detailed related issues which need address under (41.*) above. (41.f) still needs an answer ... other than permissions, is there any basis for deleting bulletins ?

Comments 10 Jan 11 13:48 pst and Response

SO Editing. Don't worry, it is not you. The site is of poor technical quality. The editing is hopeless (and believe me, I have tried to work with it and around it, to make my Answers appear even somewhat like I want them to appear). It cannot handle indents or more than one level of numbering correctly.

Delete Basis. Ok, you have a valid basis. And the users who wrote responses would not mind if they were deleted without being asked ?

(41) What you are looking for is a "cascading delete" in Standard SQL, which is defined in the Foreign Key clause (which you do not have in MyISAM). Each INSERT/UPDATE/DELETE verb applies to one table only, and may affect other tables by REFERENCE.

For non-standard SQLs, you have the DELETE multiple_table method (non-standard syntax).

First, it is very important to understand this, before anything else. The FROM and WHERE (or JOIN) clauses in a DELETE command are separate to the DELETE itself; they are in fact a SELECT. The idea is: DELETE table_one (SELECT FROM table_one, table_two WHERE join_conditions).

Therefore:

  • name the four tables in the DELETE (target)
  • name the four tables in the FROM (how you find them, via SELECT)
  • ensure you have the correct (complete) JOIN clauses for the four tables; which you can test via a SELECT
    • which means, JOIN ON OfficeCode, IssuerId, BulletinDtm (the bulletin PK affecting the thre child tables)
    • use NATURAL or INNER joins, not left joins (be explicit, do not mix them up, as a general rule)
  • ensure the WHERE identifies the specific bulletin composite Primary Key to the deleted.

Here's a link to the DELETE syntax and the JOIN syntax.

Comments 12 Jan 11 21:48 and Response

a. Don't be lazy. Write four delete statements, bottom up. That's what we have to do in the big end of town, where we do not have "cascading deletes". Write the delete for rating_response; then copy-and-paste, and delete one line of code each time. I do not understand the angst or avoidance.

b. I repeat, do not use left, right, or any kind of outer join (which is only required for the single all-encompassing delete). Use straight inner joins only (which is not a problem with 4 delete statements). Any and all upset you are experiencing is due to your need to use one delete. Give that up, and the upset and complication disappear.

  • I just wrote this code for another question. That is a single SELECT command. Three-column PK times four Subqueries. I do not understand the need to avoid long (demanded, again, due to SQL being cumbersome) commands. And I didn't even use the JOIN syntax. Took me all of ten mins to write, plus five mins to test. What exactly, is the big deal ?

c. You have not forgotten the power of Relational keys, that you recognised some weeks ago, have you. Eg. ability to grab bulletin from rating_response, without having to join with rating. If you succumb to your single-column-key desires, you will lose all that. SQL is cumbersome. But that is all we have. Deal with it. The non-SQLs try to "make life easy" but in fact, introduce all sorts of unnecessary and avoidable complications. Case in point.

Comments of 13 Jan 2011 21:18 PST and Response

Deletes. Three flavours. Great. Hopefully you will have the data values in $variables, so there will not be that form of repetition. For testing, that is fine.

  1. Delete x Four Tables. (not "Indiviudally deleting records", which is a different thing altogether; each delete except the last could net hundreds of rows). I trust the cut-and-paste took seconds. You need to be careful about forgetting to change the table names.

  2. Single Delete Command. $variables for the first triplet. You could use column names in all but the first triplet.

  3. Ok, so you will convert the SELECT to a DELETE, after testing. Left Joins. Required for the Single Delete but not otherwise. That's identical to (2) with the WHERE replaced with JOIN>

I've already recommended (1), but you are more likely to go with (3).

不疑不惑不回忆 2024-10-12 06:39:21

不要害怕加入。如果我是你,我会减少你需要编写的所有数据库逻辑,并使用像 Doctrine 或 Propel 这样的 ORM,这将使设计和维护变得更加容易——包括你试图避免的所有连接。

Dont be afraid of joins. If i were you i would cut down on all the DB logic you need to write and use an ORM like Doctrine or Propel, it will make things infinitely easier to design and maintain - including all those joins youre trying to avoid.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文