WHERE 子句中列的 DISTINCT 值的 LIMIT 不是行数 - SQL

LIMIT for DISTINCT values of column in WHERE clause not no of rows - SQL

假设我有两个表,分别命名为customerstransactions,示例数据如下:

客户

+----+---------------------------------+------------+------------+
| 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 个客户按 companyfirst_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 对我来说似乎不太正确。我正在寻求一些专家解决方案。

SQLFiddle

更新
我忘了提到我不能使用 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;