У меня есть 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
Могу ли я получить помощь, чтобы решить эту проблему, пожалуйста?
Спасибо. Я изменю. Но чтобы поделиться тем, что я пробовал, я также добавил скрипача.
Код также должен быть здесь. Нам не придется покидать сайт, чтобы получить информацию, необходимую для вашего поста. Кроме того, изображения данных бесполезны, поскольку их нельзя скопировать и вставить для создания данных, которые можно использовать для тестирования решения вашей проблемы. Вам следует опубликовать DDL для создания таблиц и DML для их заполнения вместе с SQL, который вы пытались использовать. См. минимальный воспроизводимый пример и Как задавать , а также Советы по заданию хорошего вопроса на языке структурированных запросов (SQL)
Возможно, вам понадобится Таблица. SQL Server имеет эту встроенную функцию.
Изменено как предложения
Почему число 1002 исчезло?
этот идентификатор остановился после 1/2. вот почему.
сначала вы 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
Используя ваши данные:
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
Множество способов приблизиться к этому. Здесь я просто придерживался простых союзов. Поскольку вам нужно будет вернуться к предыдущим строкам, я считаю, что самый простой способ найти неявки — это просто одновременно получить последнее значение 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;
Я предположил, что ваши значения не могут быть нулевыми. Если это окажется неправдой, скорректируйте некоторые сравнения соответствующим образом.