SELECT、SELECT COUNT 和交叉引用表可以仅由一个查询处理吗?

Posted

技术标签:

【中文标题】SELECT、SELECT COUNT 和交叉引用表可以仅由一个查询处理吗?【英文标题】:Can SELECT, SELECT COUNT and cross reference tables be handled by just one query? 【发布时间】:2017-05-29 12:11:40 【问题描述】:

我有一个显示项目列表的页面。每个项目都会显示从 mysqli 数据库中检索到的以下数据:

    标题 字幕 说明 部件号(1 个) 与该项目相关的照片总数 从项目中随机选择的一张照片 标签列表

使用分页系统每页显示 6 个项目

由于这是基于我的一个旧项目,它最初是用草率的代码(我只是在学习,不知道更好)使用许多查询完成的。第三,实际上,仅针对第 5-7 项,它们包含在与分页系统一起使用的 while 循环中。我现在很清楚,这甚至不是正确的经商方式。

我熟悉 INNER JOIN 和子查询的使用,但我担心仅使用一个选择查询可能无法获取所有这些数据,原因如下:

使用基本的SELECT 查询,第 1-4 项很容易,但是...

第 5 项需要SELECT COUNT AND...

第 6 项需要一个基本的 SELECT 查询和一个 ORDER by RAND LIMIT 1 来 从与每个项目相关的所有照片中随机选择一张照片 (使用 FilesystemIterator 是不可能的,因为照片 table 有一个列,如果照片是非活动的,则表示 0,如果是,则表示 1 活跃)

第 7 项是从标签的交叉引用表中选择的,并且 项目和包含标签 ID 和名称的表

鉴于此,我不确定这一切是否可以(甚至应该就此而言)仅通过一个查询完成,或者是否需要多个查询。我反复阅读过,在一个while循环中嵌套一个或多个查询是多么值得用报纸拍打鼻子。我什至读过多个查询通常是个坏主意。

所以我被困住了。我意识到这可能听起来太笼统了,但我没有任何有效的代码,只有使用 4 个查询来完成这项工作的旧代码,其中 3 个嵌套在 while 循环中。

数据库结构如下。

项目表:

+-------------+---------+----------+---------------+------+
| project_id  | title   | subtitle | description   | part |
|---------------------------------------------------------|
|       1     |   Chevy | Engine   | Modify        |  1   |
|       2     |   Ford  | Trans    | Rebuild       |  1   |
|       3     |   Mopar | Diff     | Swap          |  1   |
+-------------+---------+----------+---------------+------+

照片表:

+----------+------------+--------+
| photo_id | project_id | active |
|--------------------------------|
|     1    |     1      |    1   |
|     2    |     1      |    1   | 
|     3    |     1      |    1   |
|     4    |     2      |    1   |
|     5    |     2      |    1   |
|     6    |     2      |    1   |
|     7    |     3      |    1   |
|     8    |     3      |    1   |
|     9    |     3      |    1   |
+----------+------------+--------+

标签表:

+--------+------------------+
| tag_id |        tag       |
|---------------------------|
|    1   | classic          |
|    2   | new car          |
|    3   | truck            |
|    4   | performance      |
|    5   | easy             |
|    6   | difficult        |
|    7   | hard             |
|    8   | oem              |
|    9   | aftermarket      |
+--------+------------------+

标签/项目交叉引用表:

+------------+-----------+
| project_id | tag_id    |
|------------------------|
|      1     |     1     |
|      1     |     3     |
|      1     |     4     |
|      2     |     2     |
|      2     |     5     |
|      3     |     6     |
|      3     |     9     |
+------------+-----------+

我不是要求为我编写代码,但如果我的要求是有道理的,我会真诚地感谢你朝着正确的方向前进。很多时候我都在纠结于在线的 php 和 MySQLi 手册,所以如果有任何方法可以解决这个问题,那就太棒了。

非常感谢大家。

【问题讨论】:

我认为这可以通过一个查询和一个连接的子查询来完成。一次查询可以得到 1-5 和 7。让我先试试再回答 【参考方案1】:

您可以在 SELECT 子句中执行子查询,如下所示:

SELECT 
    p.title, p.subtitle, p.description, p.part,
    (SELECT COUNT(photo_id) FROM Photos where project_id = p.project_id) as total_photos, 
    (SELECT photo_id FROM Photos where project_id = p.project_id ORDER BY RAND LIMIT 1) as random_photo
FROM projects as p

现在,对于标签列表,由于它返回多行,因此您不能执行子查询,您应该为每个项目执行一次查询。好吧,实际上,如果您以某种连接形式返回所有标签,例如逗号分隔的列表:tag1,tag2,tag3... .仅当您有许多项目并且检索每个单独项目的标签列表的性能相当低时才这样做。如果你真的想要,你可以:

SELECT 
    p.title, p.subtitle, p.description, p.part,
    (SELECT COUNT(photo_id) FROM Photos where project_id = p.project_id) as total_photos, 
    (SELECT photo_id FROM Photos where project_id = p.project_id ORDER BY RAND LIMIT 1) as random_photo,
    (SELECT GROUP_CONCAT(tag SEPARATOR ', ') FROM tags WHERE tag_id in (SELECT tag_id FROM tagproject WHERE project_id = p.project_id)) as tags
FROM projects as p

【讨论】:

何利牛!我迫不及待想试试这个......让我看看我是否明白这一点......你的意思是每个项目运行上面的整个查询还是每个项目只运行一个来获取标签?现在,分页代码一次拉出 6 个项目(或任何我设置的项目)。在我看来,交叉引用表是阻碍......我理解正确吗? @mileaminute 如果您运行第二个查询,它将是满足您所有需求的单个查询,但“标签”列需要用逗号分解 (php.net/manual/en/function.explode.php) 以获得一个数组每一个标签 @leo_ap 好的,我现在正在试验它。第一个问题是,我们从哪里得到p.project_idp.title, p.subtitle, p.description, p.part,p.title, p.subtitle, p.description, p.part,p.title, p.subtitle, p.description, p.part,p.title, p.subtitle, p.description, p.part,? p.project_id 来自 FROM 项目作为 p,在查询的最后一行 这种技术总是让我着迷……不过,你已经对此有了更多的了解,我感谢你。 p 是一个占位符,可以是任何东西,对吗?我快到了……只需要炸掉标签。我假设,如果我在项目表中添加另一列,比如贡献者,我会复制标签子查询并相应地调整列名?【参考方案2】:

正如您在第 1 到第 4 项中所说的,您已经有了解决方案。

在同一查询中添加 SQL_CALC_FOUND_ROWS 而不是 SELECT COUNT 以解决第 5 项。

对于第 6 项,您可以使用 subquery 或 LEFT JOIN 来限制一个结果。

对于最新的项目,您还可以使用子查询将所有标签连接到一个结果中(例如,用逗号分隔)。

【讨论】:

以上是关于SELECT、SELECT COUNT 和交叉引用表可以仅由一个查询处理吗?的主要内容,如果未能解决你的问题,请参考以下文章

[真伪]数据库中 select count(1) 比 select count(*) 快?

引用 SQL SELECT 中的其他列

select count函数怎么用

为啥 .Select 或 .ToList() 提供嵌套 JSON,而 .Count 在父 JSON 中出现?

select count() , select count和select count(keyId)优化如何选择

select * 和select 1,select count(*)和select count的区别