Лучший способ агрегирования в значение по умолчанию - PullRequest
2 голосов
/ 15 апреля 2020

Для этого примера у меня есть три таблицы (индивидуальная, бизнес и ind_to_business). У человека есть информация о людях. Бизнес имеет информацию о бизнесе. И у ind_to_business есть информация о том, какие люди связаны с каким бизнесом. Вот их DDL:

CREATE TABLE individual
(
 ID INTEGER PRIMARY KEY,
 NAME VARCHAR2(100) NOT NULL,
 ENTERPRISE_ID VARCHAR2(25) NOT NULL UNIQUE
);
CREATE TABLE business
(
 ID INTEGER PRIMARY KEY,
 NAME VARCHAR2(100) NOT NULL,
 ENTERPRISE_ID VARCHAR2(25) NOT NULL UNIQUE
);
CREATE TABLE ind_to_business
(
  ID INTEGER PRIMARY KEY,
  IND_ID REFERENCES individual(id),
  BUS_ID REFERENCES business(id),
  START_DT DATE NOT NULL,
  END_DT DATE
);

Я ищу лучший способ отобразить одну строку для каждого человека. Если они связаны с одним бизнесом, я хочу отобразить бизнес ENTERPRISE_ID. Если они связаны более чем с одним бизнесом, я хочу отобразить значение по умолчанию «Несколько». Они всегда будут связаны с бизнесом, поэтому нет необходимости в LEFT JOIN. Они также могут быть связаны с бизнесом более одного раза (уход и возвращение). Несколько записей для одного и того же предприятия будут агрегированы.

Таким образом, для следующих образцов данных:

Физическое лицо:

+----+------------+---------------+
| ID |    NAME    | ENTERPRISE_ID |
+----+------------+---------------+
|  1 | John Smith | 53a23B7       |
|  2 | Jane Doe   | 63f2a35       |
+----+------------+---------------+

Бизнес:

+----+----------+---------------+
| ID |   NAME   | ENTERPRISE_ID |
+----+----------+---------------+
|  3 | ABC Corp | 2a34d9b       |
|  4 | XYZ Inc  | 34bf21e       |
+----+----------+---------------+

ind_to_business

+----+--------+--------+-------------+-------------+
| ID | IND_ID | BUS_ID |  START_DT   |   END_DT    |
+----+--------+--------+-------------+-------------+
|  5 |      1 |      3 | 01-JAN-2000 | 31-DEC-2002 |
|  6 |      1 |      3 | 01-JAN-2015 |             |
|  7 |      2 |      3 | 01-JAN-2000 |             |
|  8 |      2 |      4 | 01-MAR-2006 | 05-JUN-2010 |
|  9 |      2 |      4 | 15-DEC-2019 |             |
+----+--------+--------+-------------+-------------+

Я ожидал бы следующий вывод:

+---------+------------+------------+
| IND_ID  |    NAME    | LINKED_BUS |
+---------+------------+------------+
| 53a23B7 | John Smith | 2a34d9b    |
| 63f2a35 | Jane Doe   | Multiple   |
+---------+------------+------------+

Вот мой текущий запрос:

SELECT DISTINCT
       sub.ind_id,
       sub.name,
       DECODE(sub.bus_count, 1, sub.bus_id, 'Multiple') AS LINKED_BUS
FROM (SELECT i.enterprise_id AS IND_ID, 
             i.name,
             b.enterprise_id AS BUS_ID,
             COUNT(DISTINCT b.enterprise_id) OVER (PARTITION BY i.id) AS BUS_COUNT
      FROM individual i
      INNER JOIN ind_to_business i2b ON i.id = i2b.ind_id
      INNER JOIN business b ON i2b.bus_id = b.id) sub;

Мой запрос работает, но он выполняется на большой набор данных и занимает много времени для запуска. Мне интересно, есть ли у кого-нибудь идеи о том, как улучшить это, чтобы не было так много потраченной впустую обработки (т. Е. Необходимо сделать DISTINCT для конечного результата или сделать COUNT(DISTINCT) в режиме встроенного просмотра только для использования этого значения в DECODE выше).

Я также создал DBFiddle для этого вопроса. ( Ссылка )

Заранее спасибо за любой ввод.

Ответы [ 4 ]

2 голосов
/ 16 апреля 2020

Вы можете попробовать использовать коррелированный подзапрос. Это устраняет необходимость внешнего distinct:

SELECT 
    i.enterprise_id ind_id,
    i.name,
    (
        SELECT DECODE(COUNT(DISTINCT b.enterprise_id), 1, MIN(bus_id), 'Multiple')
        FROM ind_to_business i2b
        INNER JOIN business b ON i2b.bus_id = b.id
        WHERE i2b.ind_id = i.id
    ) linked_bus
FROM individual i
1 голос
/ 16 апреля 2020

Вы можете присоединиться с совокупным ind_to_business на человека. Один из способов сделать это:

select i.id, i.name, coalesce(b.enterprise_id, 'Multiple')
from individual i
join
(
  select
    ind_id,
    case when min(bus_id) = max(bus_id) then min(bus_id) else null end as bus_id
  from ind_to_business
  group by ind_id
) ib on ib.ind_id = i.id
left join business b on b.id = ib.bus_id
order by i.id;
0 голосов
/ 16 апреля 2020

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

select
    ind_id,
    name,
    case
        when count(*) > 1 then 'Multiple'
        else ind_id
    end as linked_bus
from
(
    select 
        distinct i.enterprise_id as ind_id, 
        i.name,
        b.enterprise_id as bus_id
    from individual i

    join ind_to_business i2b 
    on i.id = i2b.ind_id

    join business b 
    on i2b.bus_id = b.id
) vals

group by
    ind_id,
    name
order by
    ind_id
0 голосов
/ 16 апреля 2020

Нет необходимости использовать DISTINCT дважды. Вы можете использовать subquery factoring и поместить строчное представление в предложение WITH, а также сделать набор данных DISTINCT в самом подзапросе.

WITH data AS
(
  SELECT distinct 
       i.enterprise_id AS IND_ID, 
       i.name,
       b.enterprise_id AS BUS_ID
  FROM individual i
  JOIN ind_to_business i2b ON i.id = i2b.ind_id
  JOIN business b ON i2b.bus_id = b.id
)
SELECT ind_id,
       name,
       case 
         when count(*) = 1 then MIN(bus_id)
         else 'Multiple' 
       end AS LINKED_BUS
FROM data
GROUP BY ind_id, name;

IND_ID     NAME       LINKED_BUS               
---------- ---------- -------------------------
53a23B7    John Smith 2a34d9b                  
63f2a35    Jane Doe   Multiple
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...