我想知道以下内容:
我打算在我的(例如 - PHP)应用程序中使用它,但不想对数据库运行多个查询,我有什么选项可以从单个查询中的多个表中获取数据?
注意:我正在写这篇文章,因为我希望能够链接到我在PHP队列中经常遇到的众多问题的精心编写的指南,因此我可以在发布时更详细地链接到此答案。
答案涵盖以下内容:
答案 0 :(得分:98)
好的,我发现这篇文章非常有趣,我想分享一些关于创建查询的知识。感谢您 Fluffeh 。其他可能会阅读此内容并且可能认为我错了的人有101%可以自由编辑和批评我的答案。 (老实说,我非常感谢纠正我的错误。)
我将在MySQL
代码中发布一些常见问题。
鉴于此架构
CREATE TABLE MovieList
(
ID INT,
MovieName VARCHAR(25),
CONSTRAINT ml_pk PRIMARY KEY (ID),
CONSTRAINT ml_uq UNIQUE (MovieName)
);
INSERT INTO MovieList VALUES (1, 'American Pie');
INSERT INTO MovieList VALUES (2, 'The Notebook');
INSERT INTO MovieList VALUES (3, 'Discovery Channel: Africa');
INSERT INTO MovieList VALUES (4, 'Mr. Bean');
INSERT INTO MovieList VALUES (5, 'Expendables 2');
CREATE TABLE CategoryList
(
MovieID INT,
CategoryName VARCHAR(25),
CONSTRAINT cl_uq UNIQUE(MovieID, CategoryName),
CONSTRAINT cl_fk FOREIGN KEY (MovieID) REFERENCES MovieList(ID)
);
INSERT INTO CategoryList VALUES (1, 'Comedy');
INSERT INTO CategoryList VALUES (1, 'Romance');
INSERT INTO CategoryList VALUES (2, 'Romance');
INSERT INTO CategoryList VALUES (2, 'Drama');
INSERT INTO CategoryList VALUES (3, 'Documentary');
INSERT INTO CategoryList VALUES (4, 'Comedy');
INSERT INTO CategoryList VALUES (5, 'Comedy');
INSERT INTO CategoryList VALUES (5, 'Action');
<强>问题强>
查找属于至少 Comedy
和Romance
类别的所有电影。
<强>解决方案强>
这个问题有时候非常棘手。似乎这样的查询将是答案: -
SELECT DISTINCT a.MovieName
FROM MovieList a
INNER JOIN CategoryList b
ON a.ID = b.MovieID
WHERE b.CategoryName = 'Comedy' AND
b.CategoryName = 'Romance'
这绝对是非常错误的,因为它会产生无结果。对此的解释是,每行上只有CategoryName
的有效值。例如,第一个条件返回 true ,第二个条件始终为false。因此,通过使用AND
运算符,两个条件都应该为真;否则,它将是假的。另一个问题是这样的,
SELECT DISTINCT a.MovieName
FROM MovieList a
INNER JOIN CategoryList b
ON a.ID = b.MovieID
WHERE b.CategoryName IN ('Comedy','Romance')
并且结果仍然不正确,因为它匹配<{1>}上至少一个匹配的记录。 真实解决方案 将通过计算每部电影的记录实例数。实例的数量应与条件中提供的值的总数相匹配。
categoryName
给定架构,
SELECT a.MovieName
FROM MovieList a
INNER JOIN CategoryList b
ON a.ID = b.MovieID
WHERE b.CategoryName IN ('Comedy','Romance')
GROUP BY a.MovieName
HAVING COUNT(*) = 2
<强>问题强>
在每个软件上查找最新版本。显示以下列:CREATE TABLE Software
(
ID INT,
SoftwareName VARCHAR(25),
Descriptions VARCHAR(150),
CONSTRAINT sw_pk PRIMARY KEY (ID),
CONSTRAINT sw_uq UNIQUE (SoftwareName)
);
INSERT INTO Software VALUES (1,'PaintMe','used for photo editing');
INSERT INTO Software VALUES (2,'World Map','contains map of different places of the world');
INSERT INTO Software VALUES (3,'Dictionary','contains description, synonym, antonym of the words');
CREATE TABLE VersionList
(
SoftwareID INT,
VersionNo INT,
DateReleased DATE,
CONSTRAINT sw_uq UNIQUE (SoftwareID, VersionNo),
CONSTRAINT sw_fk FOREIGN KEY (SOftwareID) REFERENCES Software(ID)
);
INSERT INTO VersionList VALUES (3, 2, '2009-12-01');
INSERT INTO VersionList VALUES (3, 1, '2009-11-01');
INSERT INTO VersionList VALUES (3, 3, '2010-01-01');
INSERT INTO VersionList VALUES (2, 2, '2010-12-01');
INSERT INTO VersionList VALUES (2, 1, '2009-12-01');
INSERT INTO VersionList VALUES (1, 3, '2011-12-01');
INSERT INTO VersionList VALUES (1, 2, '2010-12-01');
INSERT INTO VersionList VALUES (1, 1, '2009-12-01');
INSERT INTO VersionList VALUES (1, 4, '2012-12-01');
,SoftwareName
,Descriptions
(来自VersionNo列的 ),LatestVersion
<强>解决方案强>
一些SQL开发人员错误地使用DateReleased
聚合函数。他们倾向于这样创造,
MAX()
(大多数RDBMS因为没有在SELECT a.SoftwareName, a.Descriptions,
MAX(b.VersionNo) AS LatestVersion, b.DateReleased
FROM Software a
INNER JOIN VersionList b
ON a.ID = b.SoftwareID
GROUP BY a.ID
ORDER BY a.ID
子句上指定一些非聚合列而在此处生成语法错误),结果会生成正确的group by
on每个软件但显然LatestVersion
不正确。 DateReleased
不支持MySQL
和Window Functions
,但有些RDBMS已经支持了Common Table Expression
和subquery
。此问题的解决方法是创建一个versionNo
,在每个软件上获取单个最大值SELECT a.SoftwareName, a.Descriptions,
b.LatestVersion, c.DateReleased
FROM Software a
INNER JOIN
(
SELECT SoftwareID, MAX(VersionNO) LatestVersion
FROM VersionList
GROUP BY SoftwareID
) b ON a.ID = b.SoftwareID
INNER JOIN VersionList c
ON c.SoftwareID = b.SoftwareID AND
c.VersionNO = b.LatestVersion
GROUP BY a.ID
ORDER BY a.ID
,然后在其他表上加入。
MySQL
就是这样。我将很快发布另一个,因为我记得CREATE TABLE userList
(
ID INT,
NAME VARCHAR(20),
CONSTRAINT us_pk PRIMARY KEY (ID),
CONSTRAINT us_uq UNIQUE (NAME)
);
INSERT INTO userList VALUES (1, 'Fluffeh');
INSERT INTO userList VALUES (2, 'John Woo');
INSERT INTO userList VALUES (3, 'hims056');
CREATE TABLE CONVERSATION
(
ID INT,
FROM_ID INT,
TO_ID INT,
MESSAGE VARCHAR(250),
DeliveryDate DATE
);
INSERT INTO CONVERSATION VALUES (1, 1, 2, 'hi john', '2012-01-01');
INSERT INTO CONVERSATION VALUES (2, 2, 1, 'hello fluff', '2012-01-02');
INSERT INTO CONVERSATION VALUES (3, 1, 3, 'hey hims', '2012-01-03');
INSERT INTO CONVERSATION VALUES (4, 1, 3, 'please reply', '2012-01-04');
INSERT INTO CONVERSATION VALUES (5, 3, 1, 'how are you?', '2012-01-05');
INSERT INTO CONVERSATION VALUES (6, 3, 2, 'sample message!', '2012-01-05');
标签上的任何其他常见问题解答。感谢您阅读这篇小文章。我希望你至少能从中得到一些知识。
更新1
给出架构
SELECT b.Name SenderName,
c.Name RecipientName,
a.Message,
a.DeliveryDate
FROM Conversation a
INNER JOIN userList b
ON a.From_ID = b.ID
INNER JOIN userList c
ON a.To_ID = c.ID
WHERE (LEAST(a.FROM_ID, a.TO_ID), GREATEST(a.FROM_ID, a.TO_ID), DeliveryDate)
IN
(
SELECT LEAST(FROM_ID, TO_ID) minFROM,
GREATEST(FROM_ID, TO_ID) maxTo,
MAX(DeliveryDate) maxDate
FROM Conversation
GROUP BY minFROM, maxTo
)
<强>问题强>
查找两个用户之间的最新对话。
<强>解决方案强>
{{1}}
答案 1 :(得分:58)
我想我会添加一些额外的位,用于提出的提示和技巧。
我看到的一个问题是,如何从两个表中获得不匹配的行,我看到最常被接受的答案如下所示(基于我们的汽车)和品牌表 - 其中 Holden 作为品牌列出,但未出现在汽车表中):
select
a.ID,
a.brand
from
brands a
where
a.ID not in(select brand from cars)
是它会起作用。
+----+--------+
| ID | brand |
+----+--------+
| 6 | Holden |
+----+--------+
1 row in set (0.00 sec)
但是在某些数据库中 效率不高。这是一个link to a Stack Overflow question询问它,如果你想进入细节,这里是excellent in depth article。
简短的回答是,如果优化器没有有效地处理它,使用如下的查询来获得不匹配的行可能会好得多:
select
a.brand
from
brands a
left join cars b
on a.id=b.brand
where
b.brand is null
+--------+
| brand |
+--------+
| Holden |
+--------+
1 row in set (0.00 sec)
啊,另一个老人,但是老人你不能在FROM子句中为更新指定目标表'品牌'。
MySQL不允许您在同一个表上使用子选择运行update...
查询。现在,你可能在想,为什么不把它打成where子句呢?但是,如果您只想更新其他行的max()
日期行,该怎么办?你不能在where子句中完全这样做。
update
brands
set
brand='Holden'
where
id=
(select
id
from
brands
where
id=6);
ERROR 1093 (HY000): You can't specify target table 'brands'
for update in FROM clause
所以,我们做不到那个呃?好吧,不完全是。有一个偷偷摸摸的解决方法,令人惊讶的大量用户不知道 - 虽然它确实包括一些你需要注意的hackery。
您可以将子查询粘贴到另一个子查询中,这会在两个查询之间留下足够的空隙,以便它可以工作。但是,请注意,将查询粘贴到事务中可能是最安全的 - 这将阻止在查询运行时对表进行任何其他更改。
update
brands
set
brand='Holden'
where id=
(select
id
from
(select
id
from
brands
where
id=6
)
as updateTable);
Query OK, 0 rows affected (0.02 sec)
Rows matched: 1 Changed: 0 Warnings: 0
答案 2 :(得分:18)
您可以在FROM关键字中使用多个查询的概念。让我举个例子:
SELECT DISTINCT e.id,e.name,d.name,lap.lappy LAPTOP_MAKE,c_loc.cnty COUNTY
FROM (
SELECT c.id cnty,l.name
FROM county c, location l
WHERE c.id=l.county_id AND l.end_Date IS NOT NULL
) c_loc, emp e
INNER JOIN dept d ON e.deptno =d.id
LEFT JOIN
(
SELECT l.id lappy, c.name cmpy
FROM laptop l, company c
WHERE l.make = c.name
) lap ON e.cmpy_id=lap.cmpy
您可以根据需要使用任意数量的表。即使在表子查询中,也可以在必要时使用外连接和联合。
这是一种非常简单的方法,可以涉及表格和字段。
答案 3 :(得分:6)
希望这能让你在阅读这些东西时找到表格:
<强> jsfiddle 强>
mysql> show columns from colors;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(3) | NO | PRI | NULL | auto_increment |
| color | varchar(15) | YES | | NULL | |
| paint | varchar(10) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+