Сравнение и обновление изменений – SQL

У меня есть 2 таблицы: последняя и история. В истории будут загружены все предыдущие строки, а в последней будут самые последние данные. Я хочу создать сводную таблицу на основе load_date, и в ней должно быть указано, когда было внесено изменение.

Последняя таблица:

 id   col1 col2 load_date
1001    a   g   1/3/2024
1003    q   r   1/3/2024

Таблица истории:

id    col1 col2 load_date
1001    a   b   1/1/2024
1002    d   e   1/1/2024
1001    a   g   1/2/2024

Я бы хотел, чтобы моя сводная таблица выглядела так:

id     col1 col2 load_date  change
1001    a   b    1/1/2024   new entry
1001    a   g    1/2/2024   col2 changed
1001    a   g    1/3/2024   
1002    d   e    1/1/2024   new entry
1002    d   e    1/2/2024   no show
1003    q   r    1/3/2024   new entry

У меня возникли проблемы с отслеживанием изменений и обновлением по конкретной дате. В частности, в какую дату появился идентификатор и в какую дату он появился в последний раз.

Ниже показано, что я пробовал: https://sqlfiddle.com/sql-server/online-compiler?id=5d406760-006a-4acb-9c96-e3a5236b1209

код:

create table latest(id int, col1 varchar, col2 varchar, load_date date);

        insert into latest 
        values(1001,'a','g','1/3/2024'),
              (1003,'q','r','1/3/2024');--newly showed up on 1/3

        --select * from latest;
        
        create table history(id int, col1 varchar, col2 varchar, load_date date);

        insert into history 
        values
        (1001,'a','b','1/1/2024'),
        (1002,'d','e','1/1/2024'),
        (1001,'a','g','1/2/2024');--colb changed on 1/2
        --(1002,'d','e','1/2/2024')--did not show up
        --select * from history;
        
        with combined as
     (
     select *,'latest' as source from latest l
     union all
     select *, 'history' as source from history h
     ),
     
    changes AS (
        SELECT
            ct1.id,
            ct1.col1,
            ct1.col2,
            ct1.load_date,
            CASE
                WHEN ct1.col1 <> ct2.col1 THEN 'col1 changed'
                WHEN ct1.col2 <> ct2.col2 THEN 'col2 changed'
                ELSE NULL
            END AS changes
        FROM
            combined ct1
            LEFT JOIN combined ct2
            ON ct1.id = ct2.id
    )

select * from changes

Могу ли я получить помощь, чтобы решить эту проблему, пожалуйста?

Спасибо. Я изменю. Но чтобы поделиться тем, что я пробовал, я также добавил скрипача.

Rick 22.04.2024 02:17

Код также должен быть здесь. Нам не придется покидать сайт, чтобы получить информацию, необходимую для вашего поста. Кроме того, изображения данных бесполезны, поскольку их нельзя скопировать и вставить для создания данных, которые можно использовать для тестирования решения вашей проблемы. Вам следует опубликовать DDL для создания таблиц и DML для их заполнения вместе с SQL, который вы пытались использовать. См. минимальный воспроизводимый пример и Как задавать , а также Советы по заданию хорошего вопроса на языке структурированных запросов (SQL)

Ken White 22.04.2024 02:19

Возможно, вам понадобится Таблица. SQL Server имеет эту встроенную функцию.

gbjbaanb 22.04.2024 02:20

Изменено как предложения

Rick 22.04.2024 02:20

Почему число 1002 исчезло?

shawnt00 22.04.2024 02:59

этот идентификатор остановился после 1/2. вот почему.

Rick 22.04.2024 03:00
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
7
79
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

сначала вы union обе таблицы, а затем используете оконную функцию lag() , чтобы получить значение предыдущей строки. А чтобы получить окончательный результат change, используйте выражение case для сравнения текущего значения со значением предыдущей строки.

Чтобы обработать «неявку», используйте последнюю строку из history и проверьте latest. Эта логика обрабатывается в последнем разделе запроса на объединение.

with cte as
(
  select id, col1, col2, load_date, change = null
  from   latest
  union all
  select id, col1, col2, load_date, change = null
  from   history
  union all
  select id, col1, col2, load_date = dateadd(day, 1, load_date),
         change = 'no show'
  from   (
             select id, col1, col2, load_date,
                    rn = row_number() over (partition by id order by load_date desc)
             from history 
         ) h
  where  h.rn = 1
  and    not exists
         (
             select *
             from   latest x
             where  x.id = h.id
         )
),
cte2 as
(  
  select id, col1, col2, load_date, change, 
         prev_col1 = lag(col1) over (partition by id order by load_date),
         prev_col2 = lag(col2) over (partition by id order by load_date)  
  from   cte
)
select *,
       change = isnull(change, '')
              + case when prev_col1 is null and prev_col2 is null
                     then 'new entry'
                     else ''
                     end
              + case when prev_col1 <> col1 
                     then 'col1 changed '
                     else ''
                     end
              + case when prev_col2 <> col2 
                     then 'col2 changed '
                     else ''
                     end  
from   cte2
order by id, load_date

db<>демонстрация скрипки

Используя ваши данные:

SELECT  final.*
FROM    (
        SELECT  ct1.id
        ,   ct1.col1
        ,   ct1.col2
        ,   ct1.load_date
        ,   ct1.source
        ,   LAG(ct1.col1) OVER(PARTITION BY ID ORDER BY load_date, source) AS prevCol1
        ,   LAG(ct1.col2) OVER(PARTITION BY ID ORDER BY load_date, source) AS prevCol2
        ,   LEAD(ct1.source) OVER(PARTITION BY ID ORDER BY load_date, source) AS nextSource
        FROM    (
            SELECT  *
            ,   'latest' AS source
            FROM    latest l
            UNION ALL
            SELECT  *
            ,   'history' AS source
            FROM    history h
             ) ct1
    ) combined
CROSS APPLY (
        SELECT  id, col1, col2, load_date
        ,   CONCAT_WS(', ', CASE WHEN prevCol1 IS NULL THEN 'new entry' END, CASE WHEN prevCol1 <> col1 THEN 'col1 changed' END, CASE WHEN prevCol2 <> col2 THEN 'col2 changed' END) AS changes
        UNION ALL
        SELECT  id, col1, col2, dateadd(day, 1, load_date), 'no entry'
        WHERE   nextsource IS NULL
        AND source = 'history'
) final
  1. Во-первых, вы объединяете новинки и историю с помощью UNION ALL.
  2. Затем, используя LAG/LEAD, вы можете отслеживать как предыдущие значения (для обработки изменений), так и строки без ввода.
  3. Наконец, вы берете данные, а затем создаете новую строку, используя UNION ALL для записей без предстоящей записи (это ложная строка без записи). CONCAT_WS создает строку со всеми текстами изменений, а также обрабатывает изменения col1 + col2.

Множество способов приблизиться к этому. Здесь я просто придерживался простых союзов. Поскольку вам нужно будет вернуться к предыдущим строкам, я считаю, что самый простой способ найти неявки — это просто одновременно получить последнее значение src. После того, как все данные собраны вместе, change может быть настолько подробным, насколько это необходимо, и вся эта логика содержится в определенной области кода. В зависимости от размера ваших данных производительность запросов для разных ответов может существенно различаться.

with combined as (
    select id, col1, col2, load_date, 'L' as src from latest
    union all
    select id, col1, col2, load_date, 'H' as src from history
), staggered as (
    select *,
        first_value(src) over (partition by id order by load_date desc) as last_src,
        lead(col1) over (partition by id order by load_date desc) as prior_col1,
        lead(col2) over (partition by id order by load_date desc) as prior_col2
    from combined    
), fullhistory as (
    select id, col1, col2, prior_col1, prior_col2, load_date, src
    from staggered
    union all
    select id, col1, col2, prior_col1, prior_col2, dateadd(day, 1, last_load), 'N'
    from staggered
    where last_src = 'H'
)
select id, col1, col2, load_date,
    case when prior_col1 is null then 'new entry'
         when src = 'N' then 'no show'
         when col1  = prior_col1 and col2  = prior_col2 then 'no change'
         when col1 <> prior_col1 and col2 <> prior_col2 then 'col1 & col2 changed'
         when col1 <> prior_col1 then 'col1 changed'
         when col2 <> prior_col2 then 'col2 changed'
    end as change
from fullhistory
order by id, load_date;

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

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