LIMIT для значений DISTINCT столбца в предложении WHERE, а не в строках - SQL - PullRequest
0 голосов
/ 02 сентября 2018

Допустим, у меня есть две таблицы с именами 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 |
+----+-----------+-------------+------+

Я хочу выбрать все транзакции на основе некоторого условия (см. SQL ниже) для 3 клиентов, отсортированных в алфавитном порядке их company, first_name и last_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 или подзапроса, но либо они возвращают неверные данные, либо SQL кажется мне не совсем правильным. Я ищу какое-то экспертное решение по этому вопросу.

SQLFiddle

UPDATE
Я забыл упомянуть, что не могу использовать IN для результата подзапроса, так как версия MySQL это отрицает. Эта версия MySQL еще не поддерживает подзапрос LIMIT & IN / ALL / ANY / SOME.
Пожалуйста, предоставьте альтернативу.

Ответы [ 2 ]

0 голосов
/ 02 сентября 2018

Я считаю, что это то, что вы ищете:

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;
0 голосов
/ 02 сентября 2018

Подход 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];
    }
}
...