SQL多对多选择查询

时间:2016-03-12 14:15:02

标签: php mysql pdo

有5个表

books
------
id
name
year

author
------
id
name

store
------
id
name

books_authors
------
id
book_id
author_id

books_stores
------
id
store_id
book_id

每本书都可以有很多作者,每本书都可以在几家商店里。 我希望得到所有作者以及可以找到它的所有商店的书

我的代码(我使用PDO)

if(isset($_POST['books'])){
    $books_ids = $_POST["books"];
}

$books_ids_in = implode(',', array_fill(0, count($books_ids), '?'));

$query = "SELECT b.id, b.name, b.year, a.name as author_name, s.name as store_name
          FROM books as b
          LEFT JOIN books_authors as b_a
            ON b.id = b_a.book_id
          LEFT JOIN authors as a
            ON a.id = b_a.author_id
          LEFT JOIN books_stores as b_s
            ON b.id = b_a.book_id
          LEFT JOIN stores as s
            ON s.id = b_s.store_id
          WHERE b.id IN (". $books_ids_in .")
          ORDER BY b.id";

$stmt = $conn->prepare($query); 
foreach ($books_ids as $k => $id) {
    $stmt->bindValue(($k+1), $id);
}

$stmt->execute();
$results = $stmt->fetchAll();

但是在这种情况下,如果书有两个作者(例如)和每个商店的很多字符串,我会得到两个字符串。如何为所有作者和商店的每本书获得一个结果?

2 个答案:

答案 0 :(得分:2)

您可以使用GROUP_CONCAT()功能:

SELECT
    b.id,
    b.`name`,
    b.`year`,
    GROUP_CONCAT(DISTINCT a.`name`) AS author_names,
    GROUP_CONCAT(DISTINCT s.`name`) AS store_names
FROM
    books AS b
    LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id
    LEFT JOIN authors AS a ON a.id = b_a.author_id
    LEFT JOIN books_stores AS b_s ON b.id = b_a.book_id
    LEFT JOIN stores AS s ON s.id = b_s.store_id
WHERE
    b.id IN (". $books_ids_in .")
GROUP BY b.id
ORDER BY b.id

答案 1 :(得分:1)

PIL操作的目的是产生组合爆炸。你额外的行是这种爆炸的结果。

每本书需要一行,显示作者和商店。这些行中的每一行都包含三种信息:书籍,作者,商店。因此,您需要可以为每种类型的信息每本书生成一个结果的子查询。然后你需要将这些子查询加在一起。

JOIN很容易:你已经拥有一张每行一行的表。

对于books,您需要这样:

authors

如果您知道某本书的作者应按ID顺序列出,请使用 SELECT ba.book_id, GROUP_CONCAT(a.name) author_names FROM books_authors ba LEFT JOIN authors a ON ba.author_id = a.id GROUP BY ba.book_id 。顺便提一下,作者和作者自己认为作者的顺序非常重要。

该结果集包含

之类的行
GROUP_CONCAT(a.author_name ORDER BY a.id)

你为商店做同样的事情。

  17   James Watson, Francis Crick
  19   Dewey, Cheatham, Howe

然后你将books表加入这两个子查询(a / k / a虚拟表),你就拥有了你想要的东西。

         SELECT bs.book_id, GROUP_CONCAT(s.name) store_names
           FROM books_stores bs
           LEFT JOIN stores s a ON bs.store_id = s.id
          GROUP BY bs.book_id

那会给你

SELECT b.id, b.name, aa.author_names, ss.store_names
  FROM books b
  LEFT JOIN (
             SELECT ba.book_id, GROUP_CONCAT(a.name) author_names
               FROM books_authors ba
               LEFT JOIN authors a ON ba.author_id = a.id
              GROUP BY ba.book_id
            )  aa ON b.id = aa.book_id
  LEFT JOIN (
             SELECT bs.book_id, GROUP_CONCAT(s.name) store_names
               FROM books_stores bs
               LEFT JOIN stores s a ON bs.store_id = s.id
              GROUP BY bs.book_id
            )  ss ON b.id = ss.book_id