限制WHERE子句中列的DISTINCT值,而不是行数-SQL

时间:2018-09-02 10:00:06

标签: mysql sql select limit

假设我有两个表,分别名为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 |
+----+-----------+-------------+------+

我想为3个客户根据某种条件(请参阅下面的SQL)选择所有交易,这些客户分别按其companyfirst_namelast_name的字母顺序进行排序。
因此,其想法是,在输出中可以返回多于3行,因为一个特定客户可以有多个匹配交易,但总共应该只有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或Subquery的方法,但是它们要么返回了错误的数据,要么SQL对我来说似乎不合适。我正在为此寻求一些专家解决方案。

SQLFiddle

更新
我忘了提及我不能将IN用于子查询结果,因为MySQL版本拒绝了它。 此版本的MySQL尚不支持“ LIMIT&IN / ALL / ANY / SOME子查询”。
请提供替代方法。

2 个答案:

答案 0 :(得分:1)

方法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];
    }
}

答案 1 :(得分: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中,您可以使用公用表表达式,该表表达式允许从查询中删除重复项。从https://stackoverflow.com/a/52137146/495319

查询
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;