2016-01-13 2 views
-1

РезюмеMySQL комплекса Полусоединение без группы по

Я ищу Полусоединение (МОГ) запрос, который выбирает количество клиентов и соединяет их самые последние данные из других таблиц.

В более позднее время, я хочу непосредственно добавлять условия к концу запроса: WHERE c.id IN (1,2,3)

Проблема

Насколько я знаю, мое требование исключает GROUP BY:

SELECT * FROM customer c 
LEFT JOIN customer_address ca ON ca.customer_id = c.id 
GROUP BY c.id 
# PROBLEM: Cannot append conditions *after* GROUP BY! 

С большинством попыток, основанных на подзапросах, моя проблема такая же.

В качестве дополнительной задачи я не могу строго использовать полусоединение, поскольку допускаю, по крайней мере, два типа телефонных номеров (мобильных и стационарных), которые поступают из одной таблицы. Таким образом, из телефонной таблицы я могу присоединить несколько записей на одного клиента, т. Е. Это больше не является полусоединением. Мое настоящее решение ниже иллюстрирует это.

Вопросы

  • EXPLAIN результат в нижней части выглядит производительным для меня. Я прав? Каждый из подзапросов выполняется только один раз? Обновление: Похоже, что DEPENDENT SUBQUERY выполняется один раз для каждой строки во внешнем запросе. Было бы здорово, если бы мы могли избежать этого.
  • Есть ли лучшее решение для того, что я делаю?

DDLS

DROP TABLE IF EXISTS customer; 

CREATE TABLE `customer` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY (`id`) 
); 

DROP TABLE IF EXISTS customer_address; 

CREATE TABLE `customer_address` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
    `customer_id` bigint(20) unsigned NOT NULL, 
    `street` varchar(85) DEFAULT NULL, 
    `house_number` int(10) unsigned DEFAULT NULL, 
    PRIMARY KEY (`id`) 
); 

DROP TABLE IF EXISTS customer_phone; 
CREATE TABLE `customer_phone` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
    `customer_id` bigint(20) unsigned NOT NULL, 
    `phone` varchar(32) DEFAULT NULL, 
    `type` tinyint(3) unsigned NOT NULL COMMENT '1=mobile,2=landline', 
    PRIMARY KEY (`id`) 
); 

insert ignore customer values (1); 
insert ignore customer_address values (1, 1, "OldStreet", 1),(2, 1, "NewStreet", 1); 
insert ignore customer_phone values (1, 1, "12345-M", 1),(2, 1, "12345-L-Old", 2),(3, 1, "12345-L-New", 2); 

SELECT * FROM customer; 
+----+ 
| id | 
+----+ 
| 1 | 
+----+ 

SELECT * FROM customer_address; 
+----+-------------+-----------+--------------+ 
| id | customer_id | street | house_number | 
+----+-------------+-----------+--------------+ 
| 1 |   1 | OldStreet |   1 | 
| 2 |   1 | NewStreet |   1 | 
+----+-------------+-----------+--------------+ 

SELECT * FROM customer_phone; 
+----+-------------+-------------+------+ 
| id | customer_id | phone  | type | 
+----+-------------+-------------+------+ 
| 1 |   1 | 12345-M  | 1 | 
| 2 |   1 | 12345-L-Old | 2 | 
| 3 |   1 | 12345-L-New | 2 | 
+----+-------------+-------------+------+ 

Решение до сих пор

SELECT * 
FROM customer c 

# Join the most recent address 
LEFT JOIN customer_address ca ON ca.id = (SELECT MAX(ca.id) FROM customer_address ca WHERE ca.customer_id = c.id) 

# Join the most recent mobile phone number 
LEFT JOIN customer_phone cphm ON cphm.id = (SELECT MAX(cphm.id) FROM customer_phone cphm WHERE cphm.customer_id = c.id AND cphm.`type` = 1) 

# Join the most recent landline phone number 
LEFT JOIN customer_phone cphl ON cphl.id = (SELECT MAX(cphl.id) FROM customer_phone cphl WHERE cphl.customer_id = c.id AND cphl.`type` = 2) 

# Yay conditions appended at the end 
WHERE c.id IN (1,2,3) 

Fiddle

Эта скрипка дает соответствующий набор результатов, используя данную Soluti на. См. Мои вопросы выше.

http://sqlfiddle.com/#!9/98c57/3

+1

Параметр 'where' предваряет «группа». Что так сложно в использовании правильного синтаксиса? Непонятно, что вы пытаетесь сделать. Хорошим способом объяснить проблему является использование выборочных данных и желаемых результатов. И, наконец, в чем ваш вопрос? –

+0

Трудность состоит в том, что запрос должен быть помещен в константу строки без условий. Условия затем добавляются в различные варианты использования. Я не хочу вырезать запрос в шпагате, чтобы условия были в правильном месте. До сих пор мы используем простое приложение для всех наших запросов, и я не хочу отклоняться от этого. – Timo

+0

Попробуйте добавить HAVING вместо WHERE. Надеюсь, оптимизатор разобратся. Похоже, что у вас есть кусочек рамок, но все же. – LoztInSpace

ответ

0

Я хотел бы избежать этих зависимых подзапросов, вместо того, чтобы попробовать это:

SELECT 
     * 
FROM customer c 
     LEFT JOIN (
      SELECT 
        customer_id 
       , MAX(id) AS currid 
      FROM customer_phone 
      WHERE type = 1 
      GROUP BY 
        customer_id 
    ) gm ON c.id = gm.customer_id 
     LEFT JOIN customer_phone mobis ON gm.currid = mobis.id 
     LEFT JOIN (
      SELECT 
        customer_id 
       , MAX(id) AS currid 
      FROM customer_phone 
      WHERE type = 2 
      GROUP BY 
        customer_id 
    ) gl ON c.id = gl.customer_id 
     LEFT JOIN customer_phone lands ON gl.currid = lands.id 
WHERE c.id IN (1, 2, 3) 
; 

или, возможно:

SELECT 
     * 
FROM customer c 
     LEFT JOIN (
      SELECT 
        customer_id 
       , MAX(case when type = 1 then id end) AS mobid 
       , MAX(case when type = 2 then id end) AS lndid 
      FROM customer_phone 
      GROUP BY 
        customer_id 
    ) gp ON c.id = gp.customer_id 
     LEFT JOIN customer_phone mobis ON gp.mobid = mobis.id 
     LEFT JOIN customer_phone lands ON gp.lndid = lands.id 
WHERE c.id IN (1, 2, 3) 
; 

см: http://sqlfiddle.com/#!9/ef983/1/

+0

Спасибо за это предложение. К сожалению, подзапрос выполняется сначала без каких-либо условий, поэтому он выполняет «GROUP BY» на всей таблице customer_phone, а не на небольшом подмножестве записей. – Timo

+0

У вас есть доказательства? Если вы можете это доказать, то хорошо, добавьте предложение where в подзапрос. Но прежде чем перейти к выводам, найдите доказательства, которые вам нужны. –

+0

Хе-хе, это проблема: избегая «WHERE» в середине запроса, я пытаюсь избежать этого вопроса. В этих таблицах скоро будут храниться миллионы записей, как и их предшественники, поэтому не может быть и речи о полной таблице 'GROUP BY'. Я попробовал это на таблицах такого размера, чтобы подтвердить, что это действительно то, что происходит. – Timo

Смежные вопросы