2014-11-13 4 views
-1

Чтобы упростить ситуацию, моя ситуация представляет собой ситуацию с двумя таблицами с отношением «один ко многим» (или, возможно, «один к одному» в некоторых случаях): например, счета и платежи, размещенные клиенты и заказы и т. д. Каждый платеж/заказ может быть связан только с одной учетной записью, но у учетной записи может быть ноль, 1 или несколько платежей, связанных с ней. Если я хочу, чтобы вычислить общее количество платежей/заказы на каждый счет/клиент, я бы написать что-то вроде этого:SQL: join vs где

Select c.clientid 
,coalesce(o.NumOrders,0) as NumOrders 

From clients c 

Left outer join 
(select clientid, count(*) as NumOrders from orders group by clientid) o 
on c.clientid = o.clientid 

Однако, я также видел этот тип кодирования:

Select c.clientid 
, (select count(orders.clientid) from orders where orders.clientid = c.clientid) as NumOrders 

From clients c 

Есть ли у вас замечания относительно того, какой подход был бы предпочтительным и почему?

Я считаю, что последнее труднее читать, но, возможно, это просто моя привычка. Что касается производительности, кажется, что первая быстрее, если у меня нет предложения where, но если у меня есть предложение where (например, условие, которое возвращает только 1000 записей из таблицы клиентов с 2,2 миллионами записей), то последнее кажется более быстрым.

Я использую PostgreSQL 9.1 и Microsoft SQL Server 2014. Благодарю вас!

+0

просто перепроверить запрос план выполнения –

+1

вы ответите yourselve, используйте 'join' для объединения таблиц и' where' положение, чтобы ограничить область, чтобы получить меньше записей. – Aramillo

+0

, поэтому вам не нужно присоединяться к этому примеру. Вы можете просто выбрать o.clientid, count (*) из заказов o group по o.clientid –

ответ

2

Я предпочел бы

Select c.clientid 
,count(o.clientid) as NumOrders 

From clients c 

Left outer join orders o on c.clientid = o.clientid 

group by c.clientid 

, как это просто и понятно.

Если бы я хотел выбрать одну из двух версий, я бы предпочел вторую, поскольку она короче (меньше кода нужно читать и пытаться понять), но не так сложно. Первый должен иметь дело с обработкой NULL, которая делает вещи более сложными, чем необходимо.

+0

Я согласен, что гораздо лучше избежать подзапроса, но я не думаю, что это работает. Вы хотите присоединиться к колонкам 'clientid', с одной стороны, но что более важно, вам нужно что-то вроде' count (o.orderid) ', иначе вы получите счет 1 для клиентов без заказов. –

+0

@JohnBollinger Хорошая точка! Я отредактировал свой ответ. – FrankPl

+0

Обновленная версия все равно вернет счет 1 для клиентов без каких-либо заказов. –

1

Эта версия:

Select c.clientid, 
     (select count(o.clientid) from orders o where o.clientid = c.clientid 
     ) as NumOrders 
From clients c; 

имеет большое преимущество. Ниже в значительной степени объясняет:

select c.*, . . . 

То есть, вы можете выбрать любые столбцы, которые вы любите, и вы не должны поместить их в п group by. Напоминаем, что вы не можете поставить * в group by.

В вашем случае SQL Server и Postgres имеют довольно хорошие оптимизаторы, поэтому либо они должны иметь возможность использовать индексы. Не все двигатели SQL настолько умны. MySQL, в частности, лучше использовать индекс в orders в первом случае, чем в случае group by.

Тем не менее, вторая версия в порядке, стандартный код SQL.

1

Левое соединение с производной таблицей вместо коррелированного подзапроса в предложении select, как правило, будет более эффективным. Коррелированный подзапрос заставляет зацикленный подзапрос, тогда как левое соединение может использовать циклическое или хеш-соединение. Вы хотите включить группу в производную таблицу, как в вашем примере, потому что она может использовать индекс для внешнего ключа для вычисления совокупности, тогда как группировка против простого левого соединения не будет. Если у вас есть предложение where, это зависит от того, на какую таблицу он включается. Если вы отфильтровываете таблицу заказов, убедитесь, что у вас есть предложение where внутри производной таблицы.Если вы ЗНАЧИТЕЛЬНО отфильтровываете количество строк в таблице клиентов с предложением where, то да, коррелированный подзапрос, такой как ваш второй пример, будет выполнять быстрее всего несколько зацикленных подзапросов вместо того, чтобы пытаться вычислить агрегированные итоговые значения по всему стол, который может составлять миллионы заказов. Однако я хотел бы предложить в этом случае использовать внешний атрибут для выполнения коррелированного подзапроса в предложении join, а не в предложении select, потому что он даст вам доступ к другим столбцам в таблице, если это необходимо, и не имеет реального недостатка. Поэтому я обычно рекомендую ваш первый пример:

Select c.clientid 
,coalesce(o.NumOrders,0) as NumOrders 

From clients c 

Left outer join 
(select clientid, count(*) as NumOrders from orders group by clientid) o 
on c.clientid = o.clientid