如何使用存储过程比较相同 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 示例和查询。
答案:
一个可能的解决方案是 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;
为了完成您的任务,我们将数据行转换为列,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))
我必须在同一个 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 示例和查询。
答案:
一个可能的解决方案是 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;
为了完成您的任务,我们将数据行转换为列,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)) |