SQL Server: объединение таблиц на основе значений, зависящих от записи - PullRequest
2 голосов
/ 16 октября 2019

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

Вот пример использования.

У меня есть таблица customers с информацией о клиентах и ​​таблица orders. Я хочу получить подмножество записей из orders на основе характеристик клиентов, ограниченных «самыми ранними» и «самыми поздними» датами, содержащимися в качестве данных в таблице customers. Для решения важно, чтобы я ограничил результаты своего запроса в пределах этого диапазона дат, который зависит от клиента.

ЗАКАЗЧИКИ

+------------+------------+----------+---------------------+-------------------+
| CustomerID |  Location  | Industry | EarliestActiveOrder | LatestActiveOrder |
+------------+------------+----------+---------------------+-------------------+
|        001 | New York   | Finance  | 2017-11-03          | 2019-07-30        |
|        002 | California | Tech     | 2018-06-18          | 2019-09-22        |
|        003 | New York   | Finance  | 2015-09-30          | 2019-02-26        |
|        004 | California | Finance  | 2019-02-02          | 2019-08-15        |
|        005 | New York   | Finance  | 2017-10-19          | 2018-12-20        |
+------------+------------+----------+---------------------+-------------------+

ЗАКАЗЫ

+----------+------------+------------+---------+
| OrderID | CustomerID | StartDate  | Details |
+----------+------------+------------+---------+
|     5430 |        003 | 2015-06-30 |     ... |
|     5431 |        003 | 2016-03-31 |     ... |
|     5432 |        003 | 2018-09-30 |     ... |
|     5434 |        001 | 2018-11-05 |     ... |
|     5435 |        001 | 2019-10-11 |     ... |

Пример использования, выраженный словами: «Дайте мне все активные заказы от клиентов из финансов в Нью-Йорке».

Желаемый результат - вернуть полные записи из таблицы orders для OrderID 5431,5432,5434.

Каков обычно хороший подход для структурирования запроса такого типа, учитываяorders таблица с ~ 10 ^ 6 записями?

Ответы [ 2 ]

2 голосов
/ 16 октября 2019

Вы ищете объединение:

select o.*
from orders o
inner join customers c 
    on  c.Customer_id = o.Customer_id
    and o.StartDate between c.EarliestActiveOrder  and c.LatestActiveOrder
    and c.Industry = 'Finance'
    and c.Location = 'New York'

Для производительности в этом запросе рассмотрите следующие индексы:

orders(customer_id,  StartDate)
customers(Customer_id, Industry, Location, EarliestActiveOrder, LatestActiveOrder)
1 голос
/ 16 октября 2019

Предполагая, что результирующий набор представляет собой небольшое подмножество заказов (скажем, менее 1% заказов, но 1% для иллюстрации), я бы сформулировал запрос следующим образом:

select o.*
from customers c join
     orders o
     on o.Customer_id = c.Customer_id and
        o.StartDate between c.EarliestActiveOrder  and c.LatestActiveOrder
where c.Location = 'New York' and c.industry = 'Finance';

Стратегия индексирования сложна. Для небольших наборов результатов вы, вероятно, захотите сначала ограничить клиентов, а затем найти соответствующие заказы. Этот подход предлагает индексы для:

  • customers(location, industry, customer_id, EarliestActiveOrder, LatestActiveOrder)
  • orders(customer_id, startdate)

Если бы у вас были другие столбцы для фильтрации, вам потребуются отдельные индексы дляих. Например, для industry -только фильтрации:

  • customers(industry, customer_id, EarliestActiveOrder, LatestActiveOrder)

Это может стать громоздким.

Если, с другой стороны,ваш набор результатов, вероятно, будет значительным числом заказов, тогда сканирование таблицы orders может быть более эффективным. Вы можете попробовать положиться на оптимизатор. Или просто нажмите его в правильном направлении, сформулировав запрос следующим образом:

select o.*
from orders o
where exists (select 1
              from customers c
              where o.Customer_id = c.Customer_id and
                    o.StartDate between c.EarliestActiveOrder  and c.LatestActiveOrder and
                    c.Location = 'New York' and c.industry = 'Finance'
             );

В этом случае вам нужен индекс на customers(customer_id) - но это, вероятно, уже первичный ключ, так что вы в порядке. Преимущество в том, что вам не нужно беспокоиться о точных критериях фильтрации. Недостатком является полное сканирование таблицы на orders (но не дополнительная работа для join, group by или order by).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...