2010-11-17 44 views
7

我认为自己在理解和操作C-ish语言方面相当有能力;我想出一个算法并用任何C语言实现它都不是问题。什么是构建MySQL查询的结构化方式?

编写SQL(在我的具体情况下,MySQL)查询有很大的困难。对于非常简单的查询,这不是问题,但对于复杂的查询,我会感到沮丧,不知道从哪里开始。阅读MySQL文档是很困难的,主要是因为语法描述和解释没有很好地组织。

例如,SELECT文档是各地图:它开始时的看上去像是伪BNF,但之后(因为骨料的说明文字是不能点击...喜欢select_expr)很快的不满转化这种尝试通过打开许多浏览器窗口来自定义语法的令人沮丧的练习。

足够的呜呜声。

我想知道人们如何逐步开始构建复杂的MySQL查询。这里是一个具体的例子。下面有三张表。我想SELECT一组行具有以下特点:

userInfouserProgram表,我想选择userNameisApprovedmodifiedTimestamp领域和UNION他们到一组。从这组数据中,我想要ORDER,modifiedTimestamp对于每个用户取MAX(modifiedTimestamp)(即,应该只有一行具有唯一的userName,并且与该用户名相关联的时间戳应该尽可能高)。

user表,我想匹配的firstName并与该userName,使它看起来像这样有关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 |  | 
+-------------------+--------------+------+-----+---------+-------+ 
+1

这与jQuery完全无关。已移除标记。 – casablanca 2010-11-17 00:28:28

+0

如果您想学习SQL,使用它的数据库实现文档不是很好的文献。该文档是为已经熟悉SQL的人编写的。所以,你应该找一些学习SQL的文献来代替。 – Guffa 2010-11-17 00:57:15

回答

1

对于我从你的问题明白了,你似乎需要一个相关的查询,这将是这样的:

(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”的用户数据一样,或者更复杂一些,比如你的情况:制定它的方式,这样你可以完成任务,如果你只想要最新的修改时间戳,是“使该modifiedtimestamp等于最近modifiedtimestamp”(这就是你可以很容易地采取心理捷径,错过了这一点)

第4步:如果你有聚集,它的时间来设定GROUP BY语句。例如,如果算上在USERINFO每个的firstName所有的线路,你会写“GROUP BY名字”:

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课程太长了......希望它有帮助!

+0

这并不能真正回答我的问题。我对我的例子的答案没有那么感兴趣。我对你如何回答这个问题感兴趣。如果你可以看看我的描述,然后向我展示构建查询的思维过程,那就太棒了。 – Avery 2010-11-17 01:00:47

+0

问题是:我并不完全确定我的代码是做你想做的,因为你的问题有点不清楚。我们假设它是,并且您要查找的是从查询结果开始,仅返回每个用户对应于最新modifiedTimestamp的行。它是否正确?我会修改我的答案。 – 2010-11-17 01:03:20

0

你不能不了解表中的数据和所需的逻辑结果构造SQL。没有背景给出这些表格可能的外观和意义,并且您试图收集的结果的描述对我来说没有意义,所以我不打算猜测。

关于后一点......很少有人希望将时间戳值联合使用多个来源。一般来说,当收集到类似的结果时,通常会进行某种审计/跟踪。但是,如果您丢弃关于时间戳源的所有信息并且只计算最大值,那么......具体到底是什么?无论如何,数据和期望输出的一个或多个示例,以及关于应用程序和whys的一些事情是必须使自己清楚。

在一定程度上我会让你的最终声明的形状的任何预测,(假设你的任务仍然是让每个用户一个最大时间戳)那就是它会是这个样子:

select u.firstname, u.lastname, user_max_time.userName, user_max_time.max_time 
from users u, 
(select (sometable).userName, max((sometable).(timestamp column)) 
from (data of interest) 
group by (sometable).userName) user_max_time 
where u.userName = user_max_time.userName 
order by max_time desc; 

您的任务在这里将是用user_max_time子查询内的()s替换为有意义的东西并映射到您的需求。就复杂SQL的一般方法而言,主要建议是从最内层的子查询中返回查询(一路测试以确保性能良好,并且不需要中间表)。

无论如何,如果你有麻烦,并可以回来的例子,将很乐意提供帮助。

干杯, 本

+0

您可以在SO上格式化代码,方法是突出显示它并单击“101”按钮。 – 2010-11-17 13:29:51

+0

是的,我意识到我发布的表结构的意义是不透明的。为了带来更多的亮点:对于我们的应用程序,我们将始终拥有userProgram,但userInfo中的项目非常灵活。我们合并它们的原因是我们想要发生的最后一次更新,无论它是userProgram的集合还是userInfo中的一个元素。 – Avery 2010-11-18 14:42:06

1

至于F00说,这很简单(R),如果您在设置方面认为数据的。

问题的一个问题是,预期的输出与规定的要求不符 - 描述中提到了isApproved列,但这不会出现在查询或预期输出中的任何位置。

这说明了这一点,写入查询的第一步是要清晰地知道您想要达到的什么。这个问题的最大问题在于,这个问题没有清楚地描述 - 相反,它从预期输出的样本表(如果我们有相应的预期输入数据样本会更有帮助)直接转化为你打算如何实现它。

据我了解,你想看到是用户的列表(由用户名,与其相关的名字和姓氏),连同上次任何相关的记录是在任一用户信息或userProgram修改表。

(目前尚不清楚是否要看到谁对上述任一其他表没有关联的活动的用户 - 您提供的查询意味着没有,否则加入将外连接。)

所以,你希望用户列表(由用户名,与其相关的姓和名):

SELECT firstName, lastName, userName 
FROM user 
用的时间列表

在一起的记录上次修改:

SELECT userName, MAX(modifiedTimestamp) 

...

在任一用户信息或userProgram表:

...

FROM 
(SELECT userName, modifiedTimestamp FROM userInfo 
UNION ALL 
SELECT userName, modifiedTimestamp FROM userProgram 
) subquery -- <- this is an alias 

...

通过用户名:

...

group by 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.lastNameGROUP BY子句中也没有总结。 MySQL允许使用此语法 - 在其他SQL中,因为这些字段在功能上依赖于用户名,所以在每个字段前添加MAX 将它们添加到分组中会实现相同的结果。

两三附加分:

  • UNION和UNION ALL是不相同的 - 而后者没有前者删除重复;这使得前者更加处理器密集型。 因为重复将被分组删除,所以最好使用UNION ALL。
  • 许多人会写入这个查询,因为用户加入到用户加入到userProgram的userInfo UNIONed ALL中 - 这是因为许多SQL引擎可以更有效地优化这种类型的查询。 在这一点上,这代表了不成熟的优化。
1

这里有很多好东西。感谢所有贡献的人。这是我发现有用的东西的快速总结,以及将建筑物功能连接到建筑查询的一些其他想法。我希望我可以给每个人作为优点徽章/积分,但我认为只能有一个(答案),所以我会根据积分和个人帮助来选择Traroth。

函数可以理解为三个部分:输入,过程,输出。查询可以被类似地理解。大多数查询是这个样子:

SELECT stuff FROM data WHERE data is like something 
  • SELECT部分是输出。这里有一些格式化输出的功能(即使用AS

  • FROM部分是输入。应该将输入视为一组数据;您将希望尽可能使其具体,使用适当的各种连接和子查询。

  • WHERE部分与过程相似,但与FROM部分有很多重叠。 FROMWHERE部分都可以使用各种条件适当地减少数据池,以过滤掉不需要的数据(或仅包含所需的数据)。 WHERE部分也可以帮助格式化输出。

以下是我砸开步骤:

  1. 开始思考你的输出是什么样子。这东西进入SELECT部分。

  2. 接下来,您要定义您希望使用的数据集。 Traroth指出:“知道你需要的领域,写下不同的对应表格之间的连接,这很容易......”这取决于你'易'的意思。如果你对编写查询很陌生,你可能会默认编写内部连接(就像我做的那样)。这并不总是最好的方式。 http://en.wikipedia.org/wiki/Join_(SQL)是理解不同种类的连接可能的很好的资源。

  3. 作为上一步的一部分,考虑一下数据集中较小的部分,并构建您感兴趣的完整数据集。在编写函数时,您可以编写子函数来帮助用更清晰的方式表达流程方式。与此类似,您可以编写子查询。 Mark Ba​​nnister在创建子查询和使用别名方面的一个巨大提示。您将不得不重新配置您的输出以使用此别名,但这非常关键。

  4. 最后,您可以使用各种方法来削减下来你的数据集,删除数据你不感兴趣的

一种方式来思考你所操作的数据是一个巨大的2- D矩阵:JOIN s使横向更大,UNION纵向更大。所有其他滤镜都设计得较小,以适合您的输出。我不知道JOIN是否有“功能”类比,但UNION只是将两个函数的输出加在一起。

我意识到,虽然构建查询不像编写函数那样有很多方法。例如,您可以在FROMWHERE区域中构建和削减数据集。对我来说关键是理解联接并找出如何使用别名创建子查询。