SQL查询从多个表返回数据

时间:2012-09-18 11:11:39

标签: mysql sql select

我想知道以下内容:

  • 如何从我的数据库中的多个表中获取数据?
  • 有哪些方法可以做到这一点?
  • 什么是联盟和联盟?它们如何彼此不同?
  • 我应该何时使用每一个与其他人相比?

我打算在我的(例如 - PHP)应用程序中使用它,但不想对数据库运行多个查询,我有什么选项可以从单个查询中的多个表中获取数据?

注意:我正在写这篇文章,因为我希望能够链接到我在PHP队列中经常遇到的众多问题的精心编写的指南,因此我可以在发布时更详细地链接到此答案。

答案涵盖以下内容:

  1. Part 1 - Joins and Unions
  2. Part 2 - Subqueries
  3. Part 3 - Tricks and Efficient Code
  4. Part 4 - Subqueries in the From Clause
  5. Part 5 - Mixed Bag of John's Tricks

4 个答案:

答案 0 :(得分:98)

好的,我发现这篇文章非常有趣,我想分享一些关于创建查询的知识。感谢您 Fluffeh 。其他可能会阅读此内容并且可能认为我错了的人有101%可以自由编辑和批评我的答案。 (老实说,我非常感谢纠正我的错误。

我将在MySQL代码中发布一些常见问题。


特技1号(符合多个条件的行

鉴于此架构

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');

<强>问题

查找属于至少 ComedyRomance类别的所有电影

<强>解决方案

这个问题有时候非常棘手。似乎这样的查询将是答案: -

SELECT  DISTINCT a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName = 'Comedy' AND
        b.CategoryName = 'Romance'

SQLFiddle Demo

这绝对是非常错误的,因为它会产生无结果。对此的解释是,每行上只有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')

SQLFiddle Demo

并且结果仍然不正确,因为它匹配<{1>}上至少一个匹配的记录。 真实解决方案 将通过计算每部电影的记录实例数。实例的数量应与条件中提供的值的总数相匹配。

categoryName

SQLFiddle Demo (the answer)


特技2号(每个条目的最大记录

给定架构,

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'); SoftwareNameDescriptions(来自VersionNo列的 ),LatestVersion

<强>解决方案

一些SQL开发人员错误地使用DateReleased聚合函数。他们倾向于这样创造,

MAX()

SQLFiddle Demo

大多数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不支持MySQLWindow Functions,但有些RDBMS已经支持了Common Table Expressionsubquery。此问题的解决方法是创建一个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

SQLFiddle Demo (the answer)


就是这样。我将很快发布另一个,因为我记得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


特技3号(查找两个ID之间的最新记录

给出架构

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}}

SQLFiddle Demo

答案 1 :(得分:58)

第3部分 - 技巧和高效代码

MySQL in()效率

我想我会添加一些额外的位,用于提出的提示和技巧。

我看到的一个问题是,如何从两个表中获得不匹配的行,我看到最常被接受的答案如下所示(基于我们的汽车)和品牌表 - 其中 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    |                |
+-------+-------------+------+-----+---------+----------------+