【问题标题】:Select a value inside a select statement在 select 语句中选择一个值
【发布时间】:2014-09-16 06:54:27
【问题描述】:

我有两个表:BOOKS 和 USERS_BOOKS:

书籍:

| ID | BOOKNAME |
|----|----------|
|  1 |    Book1 |
|  2 |    Book2 |
|  3 |    Book3 |
|  4 |    Book4 |
|  5 |    Book5 |

USERS_BOOKS:

| ID | USERID | BOOKID | STATUS |
|----|--------|--------|--------|
|  1 |    001 |      1 |   Read |
|  2 |    001 |      2 |   Read |
|  3 |    001 |      3 |  Added |
|  4 |    002 |      1 |  Added |
|  5 |    002 |      5 |  Added |
|  6 |    003 |      2 |   Read |
|  7 |    004 |      4 |   Read |

http://sqlfiddle.com/#!2/9cff9/1

从这个 sqlfiddle 我可以查询书籍列表和阅读它们的人数。

     select BOOKS.ID, BOOKS.BOOKNAME, 
            SUM(CASE WHEN USERS_BOOKS.STATUS='Read' THEN 1 ELSE 0 END) AS NUM_READ
       from BOOKS  
  LEFT JOIN USERS_BOOKS ON USERS_BOOKS.BOOKID = BOOKS.ID 
   GROUP BY BOOKS.ID

http://sqlfiddle.com/#!2/9cff9/1

我需要添加到此查询的是我想查看特定用户(USERID 001)是否阅读这些书。所以在第四列中,我想显示我读过第一本书(是),读过第二本书,没有读过第三、第四和第五本书(否)。 (一个人读了第四本书,但那不是我)。

期望的结果:

| ID | BOOKNAME| NUM_READ | DID_I_READ_IT|
|----|---------|----------|--------------|
|  1 |  BOOK1  |    1     |      YES     |
|  2 |  BOOK2  |    2     |      YES     |
|  3 |  BOOK3  |    0     |      NO      |
|  4 |  BOOK4  |    1     |      NO      |
|  5 |  BOOK5  |    0     |      NO      |

【问题讨论】:

  • 那是什么?查询中的两个字段?
  • 是的,这两个 ?s 是用户 ID 和状态。我在对@Ollie Jones 的回答的评论中澄清了这一点。这是否更清楚?
  • 通常使用sqlfiddle.com 来制作这类东西的原型很有帮助。但是您将示例数据呈现为图像,并将示例结果集呈现为 php 转储,因此很难进行试验。如果您提出您想要的结果,它可能会有所帮助。
  • 更清晰的问题。谢谢。
  • 但是为什么不更新你的问题,得到想要的结果!?!

标签: php mysql pdo group-by aggregate-functions


【解决方案1】:

您在滥用GROUP BY,而 MySQL 的一个有害的错误功能让您感到困惑。阅读:http://dev.mysql.com/doc/refman/5.5/en/group-by-extensions.html

以标准方式使用GROUP BY,您的结果将更加可预测。 SELECT 子句中的每个项目,如果您使用标准 GROUP BY,也必须在您的 GROUP BY 子句中命名,或者在像 SUM() 这样的聚合函数中。

您的结果集中的DID_I_ADD_IT 列并非如此。我不完全理解您要做什么,但您可以尝试将该列的代码更改为:

   GROUP_CONCAT(DISTINCT CASE WHEN USERS_BOOKS.USERID=? AND USERS_BOOKS.STATUS = ? 
                              THEN 'Yes' ELSE 'No' END) AS DID_I_ADD_IT

这会将数据移动到聚合函数中。

您还应该将您的 GROUP BY 更改为:

 GROUP BY BOOKLIST.ID, BOOKLIST.BOOK_NAME

【讨论】:

  • 我想要实现的是列出书籍,我可以看到有多少人添加了它们以及我是否添加了它们。这是否更清楚?
  • 例子:所以根据上表BOOKID 0002 1人添加,1人完成,我添加了。 BOOKID 0003 由 1 人添加,由 0 人完成,我添加。 BOOKID 0005 2人添加,0人完成,我没有添加。
  • 嗨奥利。我简化了问题并为该问题创建了一个 sqlfiddle。你现在明白我想做什么了吗?这是我的个人项目(没有工作或家庭作业),我的 sql 知识有限(还)。
【解决方案2】:

感谢您澄清您的问题。

首先,让我们考虑将确定特定用户(例如“001”)是否阅读特定书籍的查询。此查询 (http://sqlfiddle.com/#!2/9cff9/13/0) 提供了用户列表,其中包含他们至少阅读过一次的书籍。

   select DISTINCT USERID, 
                   BOOKID
     FROM USERS_BOOKS
    WHERE STATUS = 'Read'
      AND USERID='001'

现在,您需要将此查询加入到您拥有的查询中,然后只选择一个特定用户。这里的诀窍是要意识到查询/子查询/虚拟表和物理表可以互换使用

所以你得到这个复合查询 (http://sqlfiddle.com/#!2/9cff9/19/0) 来给你想要的东西。这有点复杂,因为您要逐本书地收集数据并将它们连接在一起。当聚合不同时,您需要使用不同的子查询来计算它们。

   SELECT a.ID, a.BOOKNAME, a.NUM_READ, 
          CASE WHEN b.BOOKID IS NULL THEN 'NOTREAD' ELSE 'READ' END AS DID_I_READ_IT
     FROM ( /* first subquery */
            select BOOKS.ID, BOOKS.BOOKNAME, 
                   SUM(CASE WHEN USERS_BOOKS.STATUS='Read' THEN 1 ELSE 0 END) AS NUM_READ
              from BOOKS
         LEFT JOIN USERS_BOOKS ON USERS_BOOKS.BOOKID = BOOKS.ID 
          GROUP BY BOOKS.ID, BOOKS.NAME
          ) AS a
LEFT JOIN ( /* second subquery */
            select DISTINCT USERID, 
                            BOOKID
                       FROM USERS_BOOKS
                      WHERE STATUS = 'Read'
                        AND USERID = '001'
         ) AS b ON a.ID = b.BOOKID

请注意,如果您愿意,LEFT JOIN/IS NULL 模式会生成您的规范要求的“NOTREAD”状态。

另外请注意,您仍在滥用GROUP BY。我在答案中修复了它。

【讨论】:

  • 好吧,我不确定我是否理解您的代码,但它可以工作。这不是新手级别。感谢您花时间帮助我!顺便说一句,没有更简单的方法吗?
  • @erdomester,该死!我讨厌分发人们不理解的代码。那不是SO的重点。我希望您能花一点时间来尝试了解这个 SQL 是如何由两个独立的虚拟表构成的,这些虚拟表被表述为子查询。至于你的问题,有没有“更简单”的方法?也许吧,但它可能依赖于技巧。这种方式旨在教授如何构建来自不同聚合的结果集。
  • 我还在处理这个。我设法根据自己的目标对其进行自定义,但这并不容易。
猜你喜欢
  • 2021-11-14
  • 2020-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 2013-12-29
  • 2021-02-13
  • 2017-10-14
相关资源
最近更新 更多