Как сопоставить две строки в SQL Server

#sql #sql-server #sql-server-2017

#sql #sql-сервер #sql-server-2017

Вопрос:

Рассмотрим приведенные ниже строки в таблице

 ID  | Fname  | Lname   | Age | Weight 
-----------------------------------
23  | Kareem | Benzema | 30  | 75
24  | Karim  | Benzema | 32  | 75
  

Я хочу сравнить две строки и получить разницу между ними.
Мой требуемый результат будет:

 ColumnName | OldValue | NewValue
---------------------------------
ID         | 23       | 24
---------------------------------
Fname      | Kareem   | Karim
---------------------------------
Age        | 30       | 32 
  

Комментарии:

1. И каков результат, когда есть три строки? десять строк? Это пахнет большой проблемой XY

2. сравнение всегда выполняется между двумя строками.

3. Что такое запись? Строка, столбец, значение в определенном столбце в определенной строке? В SQL нет «записей», и его использование для определения чего-либо, что я SQL, может быть неоднозначным.

4. Запись @Larnu — это строка таблицы.

5. сравнение всегда между двумя строками Означает ли это, что ваша таблица состоит только из 2 строк? Или это означает, что вы сравниваете только 2 строки из многих (даже если только 3), которые существуют в вашей таблице? И если да, то какие 2 строки вы сравниваете? Вы приводите пример, который очень очевиден для человека. Код не «видит» или «понимает» очевидное.

Ответ №1:

… приспособиться..

 declare @t table(ID int, Fname varchar(50), Lname varchar(50), Age tinyint, Weight smallint);

insert into @t(ID, Fname, Lname, Age, Weight)
values
(23, 'Kareem', 'Benzema', 30, null),
(24, 'Karim', 'Benzema', 32, 75);


select r1.[key], r1.value as oldvalue, r2.value as newvalue
from
(
    select 
        max(case when dt.rownum=1 then dt.thejson end) as row1,
        max(case when dt.rownum=2 then dt.thejson end) as row2
    from
    (   
        select 
            row_number() over(order by /*ID ?*/ @@spid) as rownum,
            (select t.* for json path, include_null_values, without_array_wrapper) thejson
        from @t as t
        where ID in (23, 24)--input
    ) as dt
) as src
cross apply openjson(src.row1) as r1
cross apply openjson(src.row2) as r2
where r1.[key] = r2.[key]
and (r1.value <> r2.value or r1.type <> r2.type) --type for null?
;
  

Ответ №2:

Хммм … позвольте мне предположить, что все ваши значения являются строками, поскольку они будут храниться в столбце. Затем отключите использование apply и используйте lag() :

 select v.columnName,
       lag(v.value) over (partition by v.columnName order by t.id) as oldValue,
       v.value as newValue
from t cross apply
     (values ('id', t.id),
              ('fname', t.vname),
              ('lname', t.lname),
              ('age', t.age),
              ('weight', t.weight)
     ) v(ColumnName, Value);
  

Наконец, вам нужны только изменения, поэтому используйте подзапрос:

 select v.*
from (select v.columnName,
             lag(v.value) over (partition by v.columnName order by t.id) as oldValue,
             v.value as newValue
      from t cross apply
           (values ('id', t.id),
                   ('fname', t.vname),
                   ('lname', t.lname),
                   ('age', t.age),
                   ('weight', t.weight)
           ) v(ColumnName, Value);
      ) v
where oldvalue <> newvalue;
  

Комментарии:

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

2. @Behnam . . . (1) Это был бы вопрос, отличный от того, который вы задали. (2) Вам нужно будет написать динамический SQL, чтобы делать то, что вы хотите.