#sql-server
#sql-сервер
Вопрос:
У меня есть следующие данные в таблице SQL Server 2017:
POS_ID Term Code Status IsActive
----------------------------------------------------
TR 101 In Progress true
TR 102 In Progress true
TR 103 In Progress true
CA 151 In Progress true
CA 152 In Progress true
DA 161 In Progress true
Требование заключается в том, что я хочу повторить каждую строку и сравнить текущую строку POS_ID
с предыдущей строкой, и если найдется другая, POS_ID
я хочу вставить 2 строки между ними таким образом, чтобы статус 1-й вставленной строки для этой предыдущей был «Выполняется» и true, а 2-я вставленная строкастатус будет прекращен и false, POS_ID
а код термина обеих этих 2 вставленных строк будет кодом термина новой POS_ID
текущей строки.
Что-то вроде в этом примере:
POS_ID Term Code Status IsActive
---------------------------------------------------------
TR 101 In Progress true
TR 102 In Progress true
TR 103 In Progress true
TR 151 In Progress true -- NEW ROW
TR 151 discontinue false -- NEW ROW
CA 151 In Progress true
CA 152 In Progress true
CA 161 In Progress true -- NEW ROW
CA 161 discontinue false -- NEW ROW
DA 161 In Progress true
Я попытался использовать функцию Lead для проверки следующего значения в строке для столбца, но не уверен, как выполнить логику реализации, как указано выше, если значения не совпадают, вставьте 2 новые строки
SELECT
POS_ID AS currentvalue,
LEAD(POS_ID) OVER (ORDER BY uniqueid) AS NextValue
FROM
dbo.input_Main_data
Создайте таблицу и вставьте скрипт
CREATE TABLE dbo.input_Main_data
(
UniqueId bigint identity(1,1),
POS_ID varchar(10),
Term_code bigint,
Status varchar(50),
IsActive bit,
CONSTRAINT PK_input_Main_data_UniqueId
PRIMARY KEY (UniqueId)
)
GO
INSERT INTO dbo.input_Main_data([POS_ID], [Term_code], [Status], [IsActive])
VALUES ('TR', 101, 'IN_PROGRESS', 1), ('TR', 102, 'IN_PROGRESS', 1),
('TR', 103, 'IN_PROGRESS', 1), ('CA', 151, 'IN_PROGRESS', 1),
('CA', 152, 'IN_PROGRESS', 1), ('DA', 161, 'IN_PROGRESS', 1)
Ответ №1:
Объединение функций lead()
and lag()
с общим табличным выражением (CTE), некоторые case
выражения и insert
оператор должны это делать.
Пример данных
create table MyData
(
uniqueid bigint IDENTITY(1,1),
POS_ID nvarchar(2),
Term_Code int,
Status nvarchar(15),
IsActive bit
);
insert into MyData (POS_ID, Term_Code, Status, IsActive) values
('TR', 101, 'In Progress', 1),
('TR', 102, 'In Progress', 1),
('TR', 103, 'In Progress', 1),
('CA', 151, 'In Progress', 1),
('CA', 152, 'In Progress', 1),
('DA', 161, 'In Progress', 1);
Решение
with cte as
(
select md.*,
coalesce(lead(md.POS_ID) over(order by md.uniqueid), md.POS_ID) as prev_POS_ID,
coalesce( lag(md.POS_ID) over(order by md.uniqueid), md.POS_ID) as next_POS_ID,
coalesce(lead(md.Term_Code) over(order by md.uniqueid), md.Term_Code) as prev_Term_Code
from MyData md
)
insert into MyData (POS_ID, Term_Code, Status, IsActive)
select c.next_POS_ID as POS_ID,
case when c.POS_ID <> c.prev_POS_ID then c.prev_Term_Code else c.Term_Code end as Term_Code,
case when c.POS_ID <> c.prev_POS_ID then c.Status else 'discontinue' end as Status,
case when c.POS_ID <> c.prev_POS_ID then c.IsActive else 0 end as IsActive
from cte c
where c.POS_ID <> c.prev_POS_ID
or c.POS_ID <> c.next_POS_ID;
Результат
POS_ID Term_Code Status IsActive
------ --------- ----------- --------
TR 101 In Progress True
TR 102 In Progress True
TR 103 In Progress True
TR 151 In Progress True
TR 151 discontinue False
CA 151 In Progress True
CA 152 In Progress True
CA 161 In Progress True
CA 161 discontinue False
DA 161 In Progress True
Поиграйте с промежуточными результатами CTE и пошаговым построением.
Комментарии:
1. Спасибо @Sander, он выдает вывод, но последовательность данных не соответствует ожидаемому результату, даже не совпадающему с результатом, которым вы поделились, поэтому вновь вставленная строка TR, т.Е. прерывание, не появляется после TR, я думаю, это в основном неправильно сортирует данные.
2. Я добавил «необычную» сортировку в конце моей скрипки, чтобы получить данные в том же порядке, что и ожидаемый результат, для упрощения проверки результата. Что происходит, так это то, что новые строки получают новые автоматические
uniqueid
значения. Отсутствие какой-либо сортировки, скорее всего, вернет данные в соответствии сuniqueid
полем (новые строки заканчиваются в конце). Возня со значениямиIDENTIY(1,1)
столбца — это совсем другая проблема! Тот, который заслуживает другого вопроса StackOverflow…3. Понял, спасибо. Но что, если мои значения в столбце POS_ID не ограничены только 3 значениями (TR, CA, DA), скажем, в фактических данных будет намного больше таких значений в таком сценарии, который ниже, не будет работать правильно? Порядок по регистру md. POS_ID когда ‘TR’, затем 1, когда ‘CA’, затем 2, когда ‘DA’, затем 3 end
4. Посмотрите на это для решения сортировки.
Ответ №2:
Новые строки, которые нужно вставить, основаны на задержке (pos_id), упорядоченной по term_code . Где задержка (pos_id)<>pos_id затем вставьте 2 строки.
Использование предоставленных образцов данных (в виде временной таблицы)
drop table if exists #input_Main_data;
go
Create table #input_Main_data
( UniqueId bigint identity(1,1),
POS_ID varchar(10),
Term_code bigint,
Status varchar(50),
IsActive bit,
CONSTRAINT PK_input_Main_data_UniqueId PRIMARY KEY (UniqueId));
go
INSERT INTO #input_Main_data([POS_ID],[Term_code], [Status], [IsActive]) Values
('TR',101,'IN_PROGRESS',1), ('TR',102,'IN_PROGRESS',1), ('TR',103,'IN_PROGRESS',1),
('CA',151,'IN_PROGRESS',1),('CA',152,'IN_PROGRESS',1),('DA',161,'IN_PROGRESS',1);
Вставить statment
with lag_cte as (
select *, lag(pos_id) over (order by term_code) lag_pos
from #input_Main_data)
insert #input_Main_data
select lag_pos, term_code, [status], isactive
from lag_cte
where pos_id<>lag_pos
union all
select lag_pos, term_code, 'discontinue', 0
from lag_cte
where pos_id<>lag_pos;
Запрос (после инструкции INSERT)
select [POS_ID],[Term_code], [Status], [IsActive]
from #input_Main_data
order by pos_id desc, term_code, isactive desc;
Вывод
POS_ID Term_code Status IsActive
TR 101 IN_PROGRESS 1
TR 102 IN_PROGRESS 1
TR 103 IN_PROGRESS 1
TR 151 IN_PROGRESS 1
TR 151 discontinue 0
DA 161 IN_PROGRESS 1
CA 151 IN_PROGRESS 1
CA 152 IN_PROGRESS 1
CA 161 IN_PROGRESS 1
CA 161 discontinue 0
Ответ №3:
Я думаю, что это может помочь :
SELECT [POS_ID],[Term Code],[Status],IsActive,[Priority] from(
select [UniqueId]
,[POS_ID]
,[Term Code]
,c.[Status]
,IsActive
,Last_Pos_ID
,c.[Priority]
from(
Select * from(
SELECT [UniqueId]
,[POS_ID]
,[Term Code]
,[Status]
,IsActive
,LAG([POS_ID],1) over(order by [UniqueId]) Last_Pos_ID
FROM dbo.input_Main_data) tmp
where Last_Pos_ID is not null and Last_Pos_ID <>[POS_ID] )b
cross apply
(
select 'In Progress' [Status],0 [Priority]
union
select 'discontinue' [Status],1 [Priority]
) c
) d
order by [UniqueId],[Priority]
Этот скрипт просто генерирует новые строки, если вы хотите, вы можете объединить их своими данными
в результате некоторые думают примерно так
Ответ №4:
Спасибо, @SteveC и @Sander Я использовал комбинацию запросов, которые были общими для вас обоих, для получения конечного результата.
Разделяем окончательный запрос здесь, поэтому он может быть полезен для тех, у кого возникнут аналогичные требования в будущем.
;with lag_cte as (
select *, lag(pos_id) over (order by term_code) lag_pos
from dbo.input_Main_data)
insert dbo.input_Main_data
select lag_pos, term_code, [status], isactive
from lag_cte
where pos_id<>lag_pos
union all
select lag_pos, term_code, 'discontinue', 0
from lag_cte
where pos_id<>lag_pos;
-- to print
;with cte_sort as
(
select md.POS_ID,
min(md.uniqueid) as sort_num
from dbo.input_Main_data md
group by md.POS_ID
)
select md.POS_ID,
md.Term_Code,
md.Status,
md.IsActive
from dbo.input_Main_data md
join cte_sort s
on s.POS_ID = md.POS_ID
order by s.sort_num,
md.Term_Code,
md.IsActive desc;