WHERE 子句中列的 DISTINCT 值的 LIMIT 不是行数 - SQL
LIMIT for DISTINCT values of column in WHERE clause not no of rows - SQL
假设我有两个表,分别命名为customers
和transactions
,示例数据如下:
客户
+----+---------------------------------+------------+------------+
| id | company | first_name | last_name |
+----+---------------------------------+------------+------------+
| 1 | Mus Donec Dignissim LLC | Tad | Hoffman |
| 2 | Aliquet Magna A LLP | Aretha | Wilkinson |
| 3 | Mauris Aliquam Foundation | Cooper | Goff |
| 4 | Quisque Libero Lacus Associates | Fulton | Beard |
| 5 | At Risus Ltd | Simone | Perkins |
| 6 | Quam Corp. | Hayfa | Hernandez |
| 7 | Vel Quam Dignissim Associates | Linus | Walker |
| 8 | A Enim Suspendisse Consulting | Emi | Wallace |
| 9 | Placerat Industries | Cody | Mendez |
| 10 | Nunc Limited | Yasir | Cunningham |
+----+---------------------------------+------------+------------+
交易
+----+-----------+-------------+------+
| id | form_type | customer_id | due |
+----+-----------+-------------+------+
| 1 | invoice | 9 | 1.08 |
| 2 | payment | 1 | 6.32 |
| 3 | invoice | 7 | 9.42 |
| 4 | payment | 9 | 3.58 |
| 5 | invoice | 7 | 5.35 |
| 6 | payment | 3 | 5.42 |
| 7 | invoice | 9 | 5.32 |
| 8 | invoice | 9 | 9.62 |
| 9 | invoice | 10 | 1.40 |
| 10 | invoice | 2 | 3.72 |
+----+-----------+-------------+------+
我想 select 根据某些条件(查看下面的 SQL)为 3 个客户按 company
、first_name
的字母顺序排序的所有交易和 last_name
分别。
因此,想法是在输出中可以有超过 3 行 returned,因为一个特定的客户可以有多个匹配的交易,但总共应该只有 3 个客户。
我知道下面的 SQL 显然错误地限制了所需数据的行数,而不是没有不同的客户,但这里是为了让问题更清楚:
SELECT
t.id AS trans_id,
c.id AS customer_id,
c.company,
c.first_name,
c.last_name,
t.due
FROM `customers` AS c, `transactions` AS t
WHERE t.due > 0 AND t.form_type = 'invoice' AND c.id = t.customer_id
ORDER BY c.company, c.first_name, c.last_name
LIMIT 0, 3
我尝试过使用 INNER JOIN
或子查询的方法,但它们 return 不正确的数据或 SQL 对我来说似乎不太正确。我正在寻求一些专家解决方案。
更新
我忘了提到我不能使用 IN
作为子查询结果,因为 MySQL 版本拒绝它。
此版本的 MySQL 尚不支持 'LIMIT & IN/ALL/ANY/SOME subquery'。
请提供替代方案。
方法 1 - 如果需要使用单个查询,并直接获取所需格式的结果:
SELECT
t.id AS trans_id,
c.id AS customer_id,
c.company,
c.first_name,
c.last_name,
t.due
FROM transactions AS t
INNER JOIN (SELECT c2.*
FROM customers AS c2
INNER JOIN transactions AS t2 ON t2.customer_id = c2.id
WHERE t2.due > 0 AND t2.form_type = 'invoice'
GROUP BY c2.id
ORDER BY c2.company, c2.first_name, c2.last_name
LIMIT 0, 3
) AS c ON c.id = t.customer_id
WHERE t.due > 0 AND
t.form_type = 'invoice'
ORDER BY c.company, c.first_name, c.last_name
方法 2 - 使用更高效的查询(减少 WHERE
条件),然后使用应用程序代码将查询结果分叉,以获得所需格式:
SELECT
c.id AS customer_id,
c.company,
c.first_name,
c.last_name,
GROUP_CONCAT(CONCAT(t.id,':',t.due)) AS trans_details
FROM customers AS c
INNER JOIN transactions AS t
WHERE t.due > 0 AND
t.form_type = 'invoice'
GROUP BY c.id
ORDER BY c.company, c.first_name, c.last_name
现在,在应用程序代码中,您可以将 trans_details
:
分叉
// $query_results is the sql result rows obtained in associative mode
foreach ($query_results as $key => $row) {
// Get all separate transactions into array
$trans_details = explode(',', $row['trans_details']);
foreach ($trans_details as $trans) {
$temp = explode(':', $trans);
// Add trans_id and due to get desired format
$query_results[$key]['trans_id'] = $temp[0];
$query_results[$key]['due'] = $temp[1];
}
}
我相信这就是您要找的:
SELECT
t.id AS trans_id,
c.id AS customer_id,
c.company,
c.first_name,
c.last_name,
t.due
FROM (
SELECT DISTINCT c.*
FROM customers AS c
INNER JOIN transactions AS t ON t.customer_id = c.id
WHERE t.due > 0
AND t.form_type = 'invoice'
ORDER BY c.company, c.first_name, c.last_name
LIMIT 3
) AS c
INNER JOIN transactions AS t ON t.customer_id = c.id
WHERE t.due > 0
AND t.form_type = 'invoice'
ORDER BY c.company, c.first_name, c.last_name;
万一使用 MySQL 更高版本的人发现这个问题,在 MySQL 8.0 中,您可以使用常见的 table 表达式,它允许从查询中删除重复项。来自
的查询
WITH cte AS (
SELECT
t.id AS trans_id,
c.id AS customer_id,
c.company,
c.first_name,
c.last_name,
t.due,
DENSE_RANK() OVER(ORDER BY c.company, c.first_name, c.last_name) AS rn
FROM customers AS c
INNER JOIN transactions AS t ON t.customer_id = c.id
WHERE t.due > 0 AND t.form_type = 'invoice'
)
SELECT * FROM cte WHERE rn <= 3;
假设我有两个表,分别命名为customers
和transactions
,示例数据如下:
客户
+----+---------------------------------+------------+------------+
| id | company | first_name | last_name |
+----+---------------------------------+------------+------------+
| 1 | Mus Donec Dignissim LLC | Tad | Hoffman |
| 2 | Aliquet Magna A LLP | Aretha | Wilkinson |
| 3 | Mauris Aliquam Foundation | Cooper | Goff |
| 4 | Quisque Libero Lacus Associates | Fulton | Beard |
| 5 | At Risus Ltd | Simone | Perkins |
| 6 | Quam Corp. | Hayfa | Hernandez |
| 7 | Vel Quam Dignissim Associates | Linus | Walker |
| 8 | A Enim Suspendisse Consulting | Emi | Wallace |
| 9 | Placerat Industries | Cody | Mendez |
| 10 | Nunc Limited | Yasir | Cunningham |
+----+---------------------------------+------------+------------+
交易
+----+-----------+-------------+------+
| id | form_type | customer_id | due |
+----+-----------+-------------+------+
| 1 | invoice | 9 | 1.08 |
| 2 | payment | 1 | 6.32 |
| 3 | invoice | 7 | 9.42 |
| 4 | payment | 9 | 3.58 |
| 5 | invoice | 7 | 5.35 |
| 6 | payment | 3 | 5.42 |
| 7 | invoice | 9 | 5.32 |
| 8 | invoice | 9 | 9.62 |
| 9 | invoice | 10 | 1.40 |
| 10 | invoice | 2 | 3.72 |
+----+-----------+-------------+------+
我想 select 根据某些条件(查看下面的 SQL)为 3 个客户按 company
、first_name
的字母顺序排序的所有交易和 last_name
分别。
因此,想法是在输出中可以有超过 3 行 returned,因为一个特定的客户可以有多个匹配的交易,但总共应该只有 3 个客户。
我知道下面的 SQL 显然错误地限制了所需数据的行数,而不是没有不同的客户,但这里是为了让问题更清楚:
SELECT
t.id AS trans_id,
c.id AS customer_id,
c.company,
c.first_name,
c.last_name,
t.due
FROM `customers` AS c, `transactions` AS t
WHERE t.due > 0 AND t.form_type = 'invoice' AND c.id = t.customer_id
ORDER BY c.company, c.first_name, c.last_name
LIMIT 0, 3
我尝试过使用 INNER JOIN
或子查询的方法,但它们 return 不正确的数据或 SQL 对我来说似乎不太正确。我正在寻求一些专家解决方案。
更新
我忘了提到我不能使用 IN
作为子查询结果,因为 MySQL 版本拒绝它。
此版本的 MySQL 尚不支持 'LIMIT & IN/ALL/ANY/SOME subquery'。
请提供替代方案。
方法 1 - 如果需要使用单个查询,并直接获取所需格式的结果:
SELECT
t.id AS trans_id,
c.id AS customer_id,
c.company,
c.first_name,
c.last_name,
t.due
FROM transactions AS t
INNER JOIN (SELECT c2.*
FROM customers AS c2
INNER JOIN transactions AS t2 ON t2.customer_id = c2.id
WHERE t2.due > 0 AND t2.form_type = 'invoice'
GROUP BY c2.id
ORDER BY c2.company, c2.first_name, c2.last_name
LIMIT 0, 3
) AS c ON c.id = t.customer_id
WHERE t.due > 0 AND
t.form_type = 'invoice'
ORDER BY c.company, c.first_name, c.last_name
方法 2 - 使用更高效的查询(减少 WHERE
条件),然后使用应用程序代码将查询结果分叉,以获得所需格式:
SELECT
c.id AS customer_id,
c.company,
c.first_name,
c.last_name,
GROUP_CONCAT(CONCAT(t.id,':',t.due)) AS trans_details
FROM customers AS c
INNER JOIN transactions AS t
WHERE t.due > 0 AND
t.form_type = 'invoice'
GROUP BY c.id
ORDER BY c.company, c.first_name, c.last_name
现在,在应用程序代码中,您可以将 trans_details
:
// $query_results is the sql result rows obtained in associative mode
foreach ($query_results as $key => $row) {
// Get all separate transactions into array
$trans_details = explode(',', $row['trans_details']);
foreach ($trans_details as $trans) {
$temp = explode(':', $trans);
// Add trans_id and due to get desired format
$query_results[$key]['trans_id'] = $temp[0];
$query_results[$key]['due'] = $temp[1];
}
}
我相信这就是您要找的:
SELECT
t.id AS trans_id,
c.id AS customer_id,
c.company,
c.first_name,
c.last_name,
t.due
FROM (
SELECT DISTINCT c.*
FROM customers AS c
INNER JOIN transactions AS t ON t.customer_id = c.id
WHERE t.due > 0
AND t.form_type = 'invoice'
ORDER BY c.company, c.first_name, c.last_name
LIMIT 3
) AS c
INNER JOIN transactions AS t ON t.customer_id = c.id
WHERE t.due > 0
AND t.form_type = 'invoice'
ORDER BY c.company, c.first_name, c.last_name;
万一使用 MySQL 更高版本的人发现这个问题,在 MySQL 8.0 中,您可以使用常见的 table 表达式,它允许从查询中删除重复项。来自
WITH cte AS (
SELECT
t.id AS trans_id,
c.id AS customer_id,
c.company,
c.first_name,
c.last_name,
t.due,
DENSE_RANK() OVER(ORDER BY c.company, c.first_name, c.last_name) AS rn
FROM customers AS c
INNER JOIN transactions AS t ON t.customer_id = c.id
WHERE t.due > 0 AND t.form_type = 'invoice'
)
SELECT * FROM cte WHERE rn <= 3;