Как использовать «Разделение по» в Oracle SQL

Я новичок в использовании «PARTITION BY» в Oracle. В структуре таблицы, в которой хранятся несколько адресов для человека, как показано ниже, как использовать «Разделение по» в Oracle SQL для

  1. получить количество записей каждого типа на человека и
  2. получить последний Addr_ID на человека каждого типа
ИДЕНТИФИКАТОР ПЕРСОНА ADDR_ID ТИП EFF_START_DATE EFF_END_DATE 6207445 7390814 ДОМ 2024-02-26 4712-12-31 6207445 0042548 ДОМ 2023-05-15 4712-12-31 6207445 6462255 ДОМ 2022-01-31 4712-12-31 6207445 6462287 ПОЧТА 2022-01-31 4712-12-31 6207445 0042571 ПОЧТА 2023-05-15 4712-12-31 1111111 0042578 ПОЧТА 2023-05-15 4712-12-31

Ожидаемый результат:

Person_ID Тип Rec_Count 6207445 ДОМ 3 6207445 ПОЧТА 2 1111111 ПОЧТА 1

Однако я запускаю скрипт, как показано ниже, и он дает другой результат.

SELECT person_id, type, 
      COUNT(*) OVER (PARTITION BY person_id) AS rec_count
FROM PER_PERSON_ADDR_USAGES_F 
WHERE
    person_id = '6207445'
ORDER BY rec_count desc
ИДЕНТИФИКАТОР ПЕРСОНА ТИП REC_COUNT 6207445 ДОМ 5 6207445 ПОЧТА 5 6207445 ДОМ 5 6207445 ПОЧТА 5 6207445 ДОМ 5

Что не так в моем SQL?

Кроме того, что мне делать, если я хочу получить последнюю версию Addr_ID для каждого человека и каждого типа?

SELECT person_id, type, 
    COUNT(*) OVER (PARTITION BY person_id) AS rec_count
FROM PER_PERSON_ADDR_USAGES_F 
WHERE
    to_number(rn) > 1
ORDER BY rec_count desc

Я пробую это, но не удалось, как показано ниже:

Query Execution started at Mon Feb 26 15:58:14 AEDT 2024
Error while executing the query:
ORA-06550: line 15, column 13:
PL/SQL: ORA-00904: "REC_COUNT": invalid identifier
ORA-06550: line 7, column 17:
PL/SQL: SQL Statement ignored

Есть идеи, что не так в моем SQL?

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

learning 26.02.2024 07:20
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
1
94
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

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

COUNT(*) OVER (PARTITION BY person_id) AS rec_count

Это означает, что вы хотите сгруппировать по person_id, но тип не включен.

Поэтому, пожалуйста, укажите тип, как показано ниже.

COUNT(*) OVER (PARTITION BY person_id, type) AS rec_count

Вот один из вариантов получить результат, ответив на оба вопроса:

WITH    --  S a m p l e    D a t a :
    PER_PERSON_ADDR_USAGES_F (PERSON_ID, ADDR_ID, TYPE, EFF_START_DATE, EFF_END_DATE) AS
        ( Select    6207445, '7390814', 'HOME', DATE '2024-02-26', DATE '4712-12-31' From Dual Union All
          Select    6207445, '0042548', 'HOME', DATE '2023-05-15', DATE '4712-12-31' From Dual Union All 
          Select    6207445, '6462255', 'HOME', DATE '2022-01-31', DATE '4712-12-31' From Dual Union All
          Select    6207445, '6462287', 'MAIL', DATE '2022-01-31', DATE '4712-12-31' From Dual Union All
          Select    6207445, '0042571', 'MAIL', DATE '2023-05-15', DATE '4712-12-31' From Dual Union All
          Select    1111111, '0042578', 'MAIL', DATE '2023-05-15', DATE '4712-12-31' From Dual 
        )
--      M a i n    S Q L :
SELECT    PERSON_ID, TYPE, PERSON_TYPE_CNT "REC_COUNT", PERSON_TYPE_LAST_ADDR "LAST ADDR_ID"
FROM    ( SELECT    PERSON_ID,  
                    TYPE,
                    COUNT(*) OVER (PARTITION BY PERSON_ID, TYPE) AS PERSON_TYPE_CNT,
                    ADDR_ID,
                    Case When Max(EFF_START_DATE) OVER(PARTITION BY PERSON_ID, TYPE) = EFF_START_DATE 
                         Then ADDR_ID
                    End AS PERSON_TYPE_LAST_ADDR, 
                    EFF_START_DATE, 
                    EFF_END_DATE
          FROM      PER_PERSON_ADDR_USAGES_F 
        )
Where    PERSON_TYPE_LAST_ADDR Is Not Null
Order By PERSON_TYPE_CNT
/*    R e s u l t :
 PERSON_ID TYPE  REC_COUNT LAST ADDR_ID
---------- ---- ---------- ------------
   1111111 MAIL          1 0042578
   6207445 MAIL          2 0042571
   6207445 HOME          3 7390814        */

ПРИМЕЧАНИЕ. Раздел «По» работает как «Группа по», но результат присутствует в каждой строке. Обычная агрегация с помощью Group By дает столько строк, сколько существует различных групп.
Выражение Case используется для определения строки с последним ADDR_ID, поэтому его можно использовать в качестве фильтра во внешнем запросе.

Ответ принят как подходящий

Аналитические функции работают со всем набором результатов и возвращают ровно столько же строк вывода, сколько и входных данных; если вы укажете PARTITION BY, то функция разделит набор результатов на разные разделы и вычислит результат внутри этого раздела, но между входными и выходными строками все равно будет однозначное соответствие.

Если вы хотите агрегировать так, чтобы на каждый раздел выводилась только одна строка, не используйте аналитические функции и используйте GROUP BY:

SELECT person_id,
       type, 
       COUNT(*) AS rec_count
FROM   PER_PERSON_ADDR_USAGES_F 
GROUP BY person_id, type
ORDER BY rec_count DESC

Что для примера данных:

CREATE TABLE PER_PERSON_ADDR_USAGES_F (PERSON_ID, ADDR_ID, TYPE, EFF_START_DATE, EFF_END_DATE) AS
SELECT 6207445, '7390814', 'HOME', DATE '2024-02-26', DATE '4712-12-31' FROM DUAL UNION ALL
SELECT 6207445, '0042548', 'HOME', DATE '2023-05-15', DATE '4712-12-31' FROM DUAL UNION ALL
SELECT 6207445, '6462255', 'HOME', DATE '2022-01-31', DATE '4712-12-31' FROM DUAL UNION ALL
SELECT 6207445, '6462287', 'MAIL', DATE '2022-01-31', DATE '4712-12-31' FROM DUAL UNION ALL
SELECT 6207445, '0042571', 'MAIL', DATE '2023-05-15', DATE '4712-12-31' FROM DUAL UNION ALL
SELECT 1111111, '0042578', 'MAIL', DATE '2023-05-15', DATE '4712-12-31' FROM DUAL;

Выходы:

ИДЕНТИФИКАТОР ПЕРСОНА ТИП REC_COUNT 6207445 ДОМ 3 6207445 ПОЧТА 2 1111111 ПОЧТА 1

Если вы хотите найти последний адрес, используйте KEEP (DENSE_RANK LAST ...):

SELECT person_id,
       type, 
       MAX(addr_id) KEEP (DENSE_RANK LAST ORDER BY eff_start_date) AS addr_id,
       COUNT(*) AS rec_count
FROM   PER_PERSON_ADDR_USAGES_F 
GROUP BY person_id, type
ORDER BY rec_count DESC

Что для примера данных выводит:

ИДЕНТИФИКАТОР ПЕРСОНА ТИП ADDR_ID REC_COUNT 6207445 ДОМ 7390814 3 6207445 ПОЧТА 0042571 2 1111111 ПОЧТА 0042578 1

Альтернативно, используйте аналитические функции, а затем фильтруйте, чтобы получить только первую строку каждого раздела:

SELECT person_id,
       type, 
       addr_id,
       rec_count
FROM   (
  SELECT person_id,
         type, 
         addr_id,
         ROW_NUMBER() OVER (PARTITION BY person_id, type ORDER BY eff_start_date DESC)
           AS rn,
         COUNT(*) OVER (PARTITION BY person_id, type) AS rec_count
  FROM   PER_PERSON_ADDR_USAGES_F 
)
WHERE  rn = 1
ORDER BY rec_count DESC

Что выводит то же, что и предыдущий запрос.

рабочий пример

Другие вопросы по теме