如何使用存储过程比较相同 table 和 return 中的两行响应数据

How to compare two row in same table and return the data in response using stored procedure

我必须在同一个 table 中使用两个 ID 比较两行,我想获取存储过程中不匹配的列及其值,我需要 return 它采用 JSON 格式。

     |Col1|Col2|Col3|Col4|
Id-1 |ABC |123 |321 |111 |
Id-2 |ABC |333 |321 |123|

输出:

     |col2|col4|
Id-1 |123 |111 |
Id-2 |333 |123 |


JSON OUTPUT Expected

[
   {
      "ColumnName":"COL2",
      "Value1":"123",
      "Value2":"333"
   },
   {
      "ColumnName":"COL4",
      "Value1":"111",
      "Value2":"123"
   }
]

我没有这方面的专业知识,但我尝试了下面的 SQL 代码,但我需要它以一种非常好的方式,而且在存储过程中也需要它,它应该被 returned JSON 格式,请帮忙!

我已经尝试过,请检查下面的 link 示例和查询。

SQL Fiddle

答案:

一个可能的解决方案是 CROSS JOIN 加上一个额外的 APPLY 运算符:

来自fiddle的测试数据:

CREATE TABLE companies ( 
   Id int,
   company_name  VARCHAR(40),
   address_type  VARCHAR(40),
   address    VARCHAR(40)
);
INSERT INTO companies VALUES (1,'Company A','Billing','111 Street');
INSERT INTO companies VALUES (2,'Company A','Shipping','112 Street');

存储过程:

CREATE PROCEDURE uspColumnsAsJson 
   @Id1 int,
   @Id2 int
AS
BEGIN
   SELECT a.ColumnName, a.Value1, a.Value2
   FROM (
      SELECT 
         c1.company_name AS company_name1, 
         c1.address_type AS address_type1, 
         c1.address AS address1, 
         c2.company_name AS company_name2, 
         c2.address_type AS address_type2, 
         c2.address AS address2 
      FROM companies c1
      CROSS JOIN companies c2
      WHERE c1.Id = @Id1 AND c2.Id = @Id2
   ) t
   CROSS APPLY (VALUES
      ('company_name', CONVERT(varchar(max), t.company_name1), CONVERT(varchar(max), t.company_name2)),
      ('address_type', CONVERT(varchar(max), t.address_type1), CONVERT(varchar(max), t.address_type2)),
      ('address', CONVERT(varchar(max), t.address1), CONVERT(varchar(max), t.address2))
   ) a (ColumnName, Value1, Value2)
   WHERE a.Value1 <> a.Value2
   FOR JSON AUTO
END

EXEC uspColumnsAsJson 1, 2

结果:

[
{"ColumnName":"address_type","Value1":"Billing","Value2":"Shipping"},
{"ColumnName":"address","Value1":"111 Street","Value2":"112 Street"}
]

更新:

如果要包含所有列,需要基于系统目录视图的动态语句:

CREATE PROCEDURE uspColumnsAsJson 
   @Id1 int,
   @Id2 int
AS
BEGIN
   DECLARE @stmt nvarchar(max)
   DECLARE @prms nvarchar(max)
   DECLARE @err int

   -- APPLY part
   SELECT @stmt = STRING_AGG(
      CONCAT(
         N'(''',
         col.[name],
         N''', CONVERT(varchar(max), c1.',
         QUOTENAME(col.[name]),
         N'), CONVERT(varchar(max), c2.',
         QUOTENAME(col.[name]),
         N'))'
      ),
      N','
   )   
   FROM sys.columns col
   JOIN sys.tables tab ON col.object_id = tab.object_id
   JOIN sys.schemas sch ON tab.schema_id = sch.schema_id
   WHERE (tab.[name] = 'companies') AND (sch.[name] = 'dbo') AND (col.[name] <> 'Id')
   
   -- Final statement
   SET @stmt = CONCAT(
      N'SELECT a.ColumnName, a.Value1, a.Value2 ',
      N'FROM companies c1 ',
      N'CROSS JOIN companies c2 ',
      N'CROSS APPLY (VALUES ',
      @stmt, 
      N') a (ColumnName, Value1, Value2) ',
      N'WHERE (c1.Id = @Id1) AND (c2.Id = @Id2) AND (a.Value1 <> a.Value2) ',
      N'FOR JSON AUTO '
   )
   
   -- Execution
   SET @prms = N'@Id1 int, @Id2 int'
   EXEC @err = sp_executesql @stmt, @prms, @Id1, @Id2
   RETURN @err
END

您需要对所有列进行逆透视,然后将每一行连接起来。

您可以使用 CROSS APPLY (VALUES

手动旋转所有内容
SELECT
  aId = a.id,
  bId = b.id,
  v.columnName,
  v.value1,
  v.value2
FROM @t a
JOIN @t b
    ON a.id < b.id
 -- alternatively
 -- ON a.id = 1 AND b.id = 2
CROSS APPLY (VALUES
   ('col1', CAST(a.col1 AS nvarchar(100)), CAST(b.col1 AS nvarchar(100))),
   ('col2', CAST(a.col2 AS nvarchar(100)), CAST(b.col2 AS nvarchar(100))),
   ('col3', CAST(a.col3 AS nvarchar(100)), CAST(b.col3 AS nvarchar(100))),
   ('col4', CAST(a.col4 AS nvarchar(100)), CAST(b.col4 AS nvarchar(100)))
) v (ColumnName, Value1, Value2)
WHERE EXISTS (SELECT v.Value1 EXCEPT SELECT v.Value2)
FOR JSON PATH;

使用 WHERE EXISTS (SELECT a.Value1 INTERSECT SELECT a.Value2) 意味着将正确考虑空值。


或者您可以使用 SELECT t.* FOR JSON 并使用 OPENJSON

逆轴
WITH allValues AS (
    SELECT
      t.id,
      j2.[key],
      j2.value,
      j2.type
    FROM @t t
    CROSS APPLY (
        SELECT t.*
        FOR JSON PATH, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER
    ) j1(json)
    CROSS APPLY OPENJSON(j1.json) j2
    WHERE j2.[key] <> 'id'
)
SELECT
  aId = a.id,
  bId = b.id,
  columnName = a.[key],
  value1 = a.value,
  value2 = b.value
FROM allValues a
JOIN allValues b ON a.[key] = b.[key]
    AND a.id < b.id
 -- alternatively
 -- AND a.id = 1 AND b.id = 2
WHERE a.type <> b.type
   OR a.value <> b.value
FOR JSON PATH;

db<>fiddle

SQL Fiddle of actual data

为了完成您的任务,我们将数据行转换为列,select 行,其中第 2 列和第 3 列不同,然后将结果集转换为 JSON 格式。在SQL中尝试处理这样的场景是迂回和复杂的,因为你需要执行一个self-join,使用CROSS APPLY(VALUES … 执行转置,然后过滤数据。代码非常long。通常的做法是从数据库中取出原始数据,然后在Python或SPL中进行处理。SPL,open-source Java包,更容易集成到Java 编程生成简洁的代码,仅用两行代码就完成了任务:

A
1 =MSSQL.query("SELECT * FROM t")
2 =json(A1.pivot@r(id;ColumnName,value).pivot(ColumnName;id,value;"id1":"Value1","id2":"Value2")
.select(#2!=#3))