什么是构建MySQL查询的结构化方式?
我认为自己在理解和操纵C-ish语言方面相当有能力; 我想出一个算法并用任何C语言实现它都不是问题。
编写SQL(在我的具体情况下,MySQL)查询时遇到巨大困难。 对于非常简单的查询,这不是问题,但对于复杂的查询,我会感到沮丧,不知道从哪里开始。 阅读MySQL文档是很困难的,主要是因为语法描述和解释没有很好地组织。
例如, SELECT
文档遍布整个地图:它开始时看起来像psuedo-BNF,但随后(因为汇总描述的文本不可点击......就像select_expr
),它很快就会投入到这个令人沮丧的练习中试图通过打开许多浏览器窗口来将语法拼凑在一起。
足够的呜呜声。
我想知道人们如何逐步开始构建复杂的MySQL查询。 这是一个具体的例子。 下面有三张表。 我想SELECT
一组具有以下特征的行:
从userInfo
和userProgram
表中,我想选择userName
, isApproved
和modifiedTimestamp
字段并将它们UNION
到一个集合中。 从这个集合中,我想通过modifiedTimestamp
对每个用户采用MAX(modifiedTimestamp)
进行ORDER
(即应该只有一行具有唯一的userName
并且与该用户名关联的时间戳应该尽可能高)。
在user
表中,我想匹配与userName
关联的firstName
和lastName
,使其看起来像这样:
+-----------+----------+----------+-------------------+
| firstName | lastName | userName | modifiedTimestamp |
+-----------+----------+----------+-------------------+
| JJ | Prof | jjprofUs | 1289914725 |
| User | 2 | user2 | 1289914722 |
| User | 1 | user1 | 1289914716 |
| User | 3 | user3 | 1289914713 |
| User | 4 | user4 | 1289914712 |
| User | 5 | user5 | 1289914711 |
+-----------+----------+----------+-------------------+
我得到的最接近的查询是这样的:
(SELECT firstName, lastName, user.userName, modifiedTimestamp
FROM user, userInfo
WHERE user.userName=userInfo.userName)
UNION
(SELECT firstName, lastName, user.userName, modifiedTimestamp
FROM user, userProgram
WHERE user.userName=userProgram.userName)
ORDER BY modifiedTimestamp DESC;
我觉得我很接近,但我不知道该从哪里走,或者即使我以正确的方式思考这件事。
> user
+--------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+---------+-------+
| userName | char(8) | NO | PRI | NULL | |
| firstName | varchar(255) | NO | | NULL | |
| lastName | varchar(255) | NO | | NULL | |
| email | varchar(255) | NO | UNI | NULL | |
| avatar | varchar(255) | YES | | '' | |
| password | varchar(255) | NO | | NULL | |
| passwordHint | text | YES | | NULL | |
| access | int(11) | NO | | 1 | |
| lastLoginTimestamp | int(11) | NO | | -1 | |
| isActive | tinyint(4) | NO | | 1 | |
+--------------------+--------------+------+-----+---------+-------+
> userInfo
+-------------------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+------------+------+-----+---------+-------+
| userName | char(8) | NO | MUL | NULL | |
| isApproved | tinyint(4) | NO | | 0 | |
| modifiedTimestamp | int(11) | NO | | NULL | |
| field | char(255) | YES | | NULL | |
| value | text | YES | | NULL | |
+-------------------+------------+------+-----+---------+-------+
> userProgram
+-------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+--------------+------+-----+---------+-------+
| userName | char(8) | NO | PRI | NULL | |
| isApproved | tinyint(4) | NO | PRI | 0 | |
| modifiedTimestamp | int(11) | NO | | NULL | |
| name | varchar(255) | YES | | NULL | |
| address1 | varchar(255) | YES | | NULL | |
| address2 | varchar(255) | YES | | NULL | |
| city | varchar(50) | YES | | NULL | |
| state | char(2) | YES | MUL | NULL | |
| zip | char(10) | YES | | NULL | |
| phone | varchar(25) | YES | | NULL | |
| fax | varchar(25) | YES | | NULL | |
| ehsChildren | int(11) | YES | | NULL | |
| hsChildren | int(11) | YES | | NULL | |
| siteCount | int(11) | YES | | NULL | |
| staffCount | int(11) | YES | | NULL | |
| grantee | varchar(255) | YES | | NULL | |
| programType | varchar(255) | YES | | NULL | |
| additional | text | YES | | NULL | |
+-------------------+--------------+------+-----+---------+-------+
从我的理解来看,你似乎需要一个相关的查询,如下所示:
(SELECT firstName, lastName, user.userName, modifiedTimestamp
FROM user, userInfo ui1
WHERE user.userName=userInfo.userName
AND modifiedtimestamp=(select max(modifiedtimestamp) from userInfo ui2 where ui1.userName=ui2.userName))
UNION
(SELECT firstName, lastName, user.userName, modifiedTimestamp
FROM user, userProgram up1
WHERE user.userName=userProgram.userName
AND modifiedtimestamp=(select max(modifiedtimestamp) from userProgram up2 where up1.userName=up2.userName))
ORDER BY modifiedTimestamp DESC;
那么,我要继续去获得这个结果吗? 关键是:明确表达您想要检索的信息,而不需要采取精神捷径。
第1步:选择我需要在我的数据库的不同表中的字段。 这就是SELECT和FROM之间的内容。 看起来很明显,但它涉及聚合函数(如总和或计数)时变得不那么明显。 在这种情况下,你必须说,例如“我需要每个firstName的userInfo中的行数”。 请参阅下面的GROUP BY。
第2步:了解您需要的字段,编写不同相应表格之间的连接。 这是一个容易的...
步骤3:表达你的条件。 它可以很容易,就像你希望userName =“RZEZDFGBH”的用户数据一样,或者更复杂一些,比如你的情况:制定它的方式,这样你可以完成任务,如果你只想要最新的修改时间戳,是“这样修改时间戳等于最近修改时间戳”(这是你可以轻松地采取心理捷径并错过点的地方)
第4步:如果您有聚合,现在可以设置GROUP BY语句。 例如,如果您计算每个firstName的userInfo中的所有行,则会编写“GROUP BY firstName”:
SELECT firstName,count(*) FROM userInfo GROUP BY firstName
这会为您提供表中每个不同firstName的条目数。
步骤5:有条件。 这些是聚合物的条件。 在前面的例子中,如果你只想在表中有超过5行的firstName的数据,你可以编写SELECT firstName,count(*) FROM userInfo GROUP BY firstName HAVING count(*)>5
第6步:用ORDER BY排序。 挺容易...
这只是一个简短的总结。 还有很多更多的发现,但是在这里编写完整的SQL课程太长了......希望它有帮助!
正如f00所说,如果按照集合来考虑数据,则很简单(r)。
问题的一个问题是,预期的输出与规定的要求不符 - 描述中提到了isApproved列,但这不会出现在查询或预期输出中的任何位置。
这说明了编写查询的第一步是清楚地知道你想实现什么 。 现在问题的更大问题是这个问题没有清楚地描述 - 相反,它从预期输出的样本表(如果我们有相应的预期输入数据样本会更有帮助)直接描述如何你打算实现它。
据我了解,你希望看到什么是用户的列表(由用户名,与其相关的姓和名),与上一次的任何相关的记录是在任一用户信息或userProgram表修改在一起。
(目前尚不清楚您是否希望看到在这些其他表中没有关联活动的用户 - 您提供的查询意味着不是,否则连接将是外连接。)
所以,你需要一个用户列表(通过用户名和他们的名字和姓氏):
SELECT firstName, lastName, userName
FROM user
以及上次修改记录的时间列表:
SELECT userName, MAX(modifiedTimestamp)
...
在userInfo或userProgram表上:
...
FROM
(SELECT userName, modifiedTimestamp FROM userInfo
UNION ALL
SELECT userName, modifiedTimestamp FROM userProgram
) subquery -- <- this is an alias
...
按用户名:
...
group by userName
这两组数据需要通过它们的userName进行关联 - 因此最终查询变为:
SELECT user.firstName, user.lastName, user.userName,
MAX(subquery.modifiedTimestamp) last_modifiedTimestamp
FROM user
JOIN
(SELECT userName, modifiedTimestamp FROM userInfo
UNION ALL
SELECT userName, modifiedTimestamp FROM userProgram
) subquery
ON user.userName = subquery.userName
GROUP BY user.userName
在大多数SQL版本中,此查询将返回一个错误,因为user.firstName
和user.lastName
未包含在GROUP BY
子句中,也未汇总。 MySQL允许使用这种语法 - 在其他SQL中,因为这些字段在功能上依赖于userName,所以在每个字段前添加MAX或将它们添加到分组中会得到相同的结果。
其他几点:
这里有很多好东西。 感谢所有贡献的人。 这是我发现有用的东西的快速总结,以及将建筑物功能连接到建筑查询的一些其他想法。 我希望我可以给每个人作为优点徽章/积分,但我认为只能有一个(答案),所以我会根据积分和个人帮助来选择Traroth。
一个功能可以被理解为三个部分:输入,过程,输出。 查询可以被类似地理解。 大多数查询看起来像这样:
SELECT stuff FROM data WHERE data is like something
SELECT
部分是输出。 这里有一些格式化输出的功能(即使用AS
)
FROM
部分是输入。 应该将输入视为一组数据; 您将希望尽可能使其具体,使用适当的各种连接和子查询。
WHERE
部分与过程相似,但与FROM部分有很多重叠。 FROM
和WHERE
部分都可以使用各种条件适当地减少数据池,以过滤掉不需要的数据(或仅包含所需的数据)。 WHERE
部分也可以帮助格式化输出。
以下是我如何分解步骤:
首先考虑你的输出是什么样的。 这东西进入SELECT
部分。
接下来,您想要定义您想要处理的数据集。 Traroth指出:“知道你需要的领域,写下不同的对应表格之间的连接,这很容易......”这取决于你'易'的意思。 如果你对编写查询很陌生,你可能会默认编写内部连接(就像我做的那样)。 这并不总是最好的方式。 http://en.wikipedia.org/wiki/Join_(SQL)是理解不同种类的连接可能的很好的资源。
作为前一步骤的一部分,考虑数据集的较小部分,并建立您感兴趣的完整数据集。在编写函数时,您可以编写子函数以帮助更清晰地表达流程。 与此类似,您可以编写子查询。 Mark Bannister在创建子查询和使用别名方面的一个巨大提示。 您将不得不重新配置您的输出以使用此别名,但这非常关键。
最后,您可以使用各种方法来削减数据集,删除不感兴趣的数据
考虑你所操作的数据的一种方法是一个巨大的2D矩阵: JOIN
使水平方向变大, UNION
增大垂直方向。 所有其他滤镜都设计得较小,以适合您的输出。 我不知道JOIN
是否有“功能”类比,但UNION
只是将两个函数的输出结合在一起。
不过,我意识到,构建查询的方法有很多,不像写一个函数。 例如,您可以在FROM
和WHERE
区域中构建和削减您的数据集。 对我来说关键是理解联接并找出如何使用别名创建子查询。