有没有办法从 SQL 中的一组列中获取所有非空值?
Is there a way to get all non null value from a group of columns in SQL?
美好的一天,
请问,有没有更简单的方法获取一组不为空的列?
场景
我有一个从 excel 获取数据的上传器。该代码正在运行,但陷入僵局。我想修改我的代码,因为我正在做平均 RBAR(逐行痛苦)
代码
ALTER procedure [dbo].[saveProdPropSampDta]
@PartNo as nvarchar(20),
@PPFno as nvarchar(20),
@Dimension as nvarchar(30),
@ToUpdate as nvarchar(10),
@count as nvarchar(2),
@Judgement as nvarchar(2)
as
declare @TSQL as nvarchar(max)
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
begin
--remove spaces from variable @dimension
set @Dimension = REPLACE(@Dimension, ' ','')
--ToUpdate is the value of from Excel
set @TSQL = 'update ProductProperty set T'+@count+' = @ToUpdate where Partno = @Partno and PPFno = @PPFno and DName = @Dimension';
exec sp_executesql @TSQL, N'@PartNo nvarchar(20), @PPFno nvarchar(20), @Dimension as nvarchar(30), @ToUpdate as nvarchar(10), @count as nvarchar(2)', @PartNo, @PPFno , @Dimension, @ToUpdate, @count;
declare @cursor CURSOR
declare @colname as nvarchar(30)
declare @top as integer
declare @query as nvarchar(MAX)
declare @topass as nvarchar(MAX) = ''
declare @retVal nvarchar(30)
declare @categoryid NVARCHAR(MAX)
SET @cursor = CURSOR LOCAL FOR
(select [Name] from sys.columns where object_id = (select object_id from sys.tables where name = 'ProductProperty') and [Name] like 'T%' and [name] <> 'TEMP')order by [Name] asc
--Gets column names from table from T1 -> T45
OPEN @cursor
FETCH NEXT
FROM @cursor INTO @colname
WHILE @@FETCH_STATUS = 0
BEGIN
SET @query = N'set @categoryid = (select distinct ' + @colname + ' from ProductProperty
where PartNo = @PartNo and PPFNo = @PPFno and DName = @Dimension)';
--
EXEC sp_executesql @query,
N'@PartNo nvarchar(20), @PPFno nvarchar(20),@Dimension
nvarchar(30), @categoryid NVARCHAR(MAX) OUTPUT', @PartNo, @PPFno,@Dimension,
@categoryid OUTPUT
if @categoryid is not null
begin
set @topass = @topass + @colname + '+'
end
FETCH NEXT
FROM @cursor INTO @colName
END
CLOSE @cursor
DEALLOCATE @cursor
set @topass = LEFT(@topass, LEN(@topass)-1)
set @topass = N'set @categoryid = (Select cast(('+@topass+')/'+@count+' as decimal(18,3)) from ProductProperty where PartNo = @PartNo and PPFNo = @PPFno and DName = @Dimension)';
EXEC sp_executesql @topass,
N'@PartNo nvarchar(20), @PPFno nvarchar(20),@Dimension
nvarchar(30), @categoryid NVARCHAR(MAX) OUTPUT', @PartNo, @PPFno,@Dimension,
@categoryid OUTPUT
set @topass = 'update ProductProperty set average = '+@categoryid+', Judgement = '''+@Judgement+''' where PartNo = @PartNo and PPFNo = @PPFno and DName = @Dimension';
EXEC sp_executesql @topass,
N'@PartNo nvarchar(20), @PPFno nvarchar(20),@Dimension
nvarchar(30)', @PartNo, @PPFno,@Dimension
end
这样的代码的原因
代码在VB.Net代码中每次调用时都会运行,它会从变量中获取数据到存储过程中。该代码实际上从非空的列中获取平均值
我想要什么
有没有类似
select ave(ifnull(T1.....T2,If Null dont include, ifnot null include)) from TABLE
我猜你需要这样的东西。
SELECT AVG(CASE WHEN T1 IS NOT NULL THEN T1 ELSE 0 END)
FROM TABLE
我可能会采用几种方法来重新设计您的解决方案。
如果您可以更改的内容有限,那么#2 可能是门票。但我个人的偏好是 #1 - 生成的代码更简单,如果您决定需要 T46,则无需进行任何更改。
方法编号 1:
您没有提供 table 的完整详细信息,因此我猜测您有一个构成主键的 ID
列,否则我们'我将在新解决方案中添加一个 :)
我还假设您接收 nvarchar
参数到您的存储过程是必要的而不是选择,并相应地声明数据类型。
是否无法重新设计您的数据模型?解决此问题的一种方法是将您的 "T1".."T45" 列移动到单独的 table 并将它们视为行。
您可以从 table 的:
Create Table ProductProperty
(
ID int identity,
Partno nvarchar(20),
PPFno nvarchar(20),
DName nvarchar(30),
T1 decimal(18, 3),
T2 decimal(18, 3),
T3 decimal(18, 3),
-- ...
T44 decimal(18, 3),
T45 decimal(18, 3),
average decimal(18, 3),
Judgement nvarchar(2)
)
至
Create Table ProductProperty
(
ID int identity,
Partno nvarchar(20),
PPFno nvarchar(20),
DName nvarchar(30),
average decimal(18, 3),
Judgement nvarchar(2)
) -- You would probably want to put a unique index on (Partno, PPFno, DName)
Create Table ProductPropertyT
(
ProductPropertyID int,
TNo nvarchar(2),
TValue decimal(18, 3)
)
现在您的存储过程只需要识别主键值(基于我对复合键 Partno
、PPFno
和 DName
的推测),创建或更新记录ProductPropertyT
table,并对 TValue
具有匹配 ProductPropertyId
值的记录进行简单平均:
alter procedure [dbo].[saveProdPropSampDta]
@PartNo as nvarchar(20),
@PPFno as nvarchar(20),
@Dimension as nvarchar(30),
@ToUpdate as nvarchar(10),
@count as nvarchar(2),
@Judgement as nvarchar(2)
as
begin
--remove spaces from variable @dimension
set @Dimension = REPLACE(@Dimension, ' ','')
-- determine the PK
declare @pk int
select @pk = ID from ProductProperty where Partno = @Partno and PPFno = @PPFno and DName = @Dimension
-- create / update the value in the ProductPropertyT table
if exists (select * from ProductPropertyT where ProductPropertyID = @pk and TNo = @count)
begin
update ProductPropertyT
set TValue = cast(@ToUpdate as decimal(18, 3))
where ProductPropertyID = @pk and TNo = @count
end
else
begin
insert into ProductPropertyT (ProductPropertyID, TNo, TValue)
values (@pk, @count, cast(@ToUpdate as decimal(18, 3)))
end
-- update the average and judgement in the ProductProperty table.
update ProductProperty
set average = (select avg(TValue) from ProductPropertyT where ProductPropertyID = @pk)
,Judgement = @Judgement
where ID = @pk
end
存储过程现在:更短;没有动态 sql;很容易理解。
方法编号 2:
如果您绝对必须拥有此数据模型,那么您可以使用 unpivot
从 T1...T45 列获取非空值并简单地对它们进行平均。
SELECT avg(TValue)
from
(
-- this will give you only the values for the current part/ppf/dimension
select * from ProductProperty
where Partno = @Partno and PPFno = @PPFno and DName = @Dimension
) myrow
unpivot
(
TValue
for TCol in (T1, T2, ... T44, T45)
) as x
我将把它作为练习留给您,将其编织到更新语句中 - 但足以说明您的这部分过程不需要任何动态 sql。
希望对您有所帮助。
美好的一天,
请问,有没有更简单的方法获取一组不为空的列?
场景
我有一个从 excel 获取数据的上传器。该代码正在运行,但陷入僵局。我想修改我的代码,因为我正在做平均 RBAR(逐行痛苦)
代码
ALTER procedure [dbo].[saveProdPropSampDta]
@PartNo as nvarchar(20),
@PPFno as nvarchar(20),
@Dimension as nvarchar(30),
@ToUpdate as nvarchar(10),
@count as nvarchar(2),
@Judgement as nvarchar(2)
as
declare @TSQL as nvarchar(max)
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
begin
--remove spaces from variable @dimension
set @Dimension = REPLACE(@Dimension, ' ','')
--ToUpdate is the value of from Excel
set @TSQL = 'update ProductProperty set T'+@count+' = @ToUpdate where Partno = @Partno and PPFno = @PPFno and DName = @Dimension';
exec sp_executesql @TSQL, N'@PartNo nvarchar(20), @PPFno nvarchar(20), @Dimension as nvarchar(30), @ToUpdate as nvarchar(10), @count as nvarchar(2)', @PartNo, @PPFno , @Dimension, @ToUpdate, @count;
declare @cursor CURSOR
declare @colname as nvarchar(30)
declare @top as integer
declare @query as nvarchar(MAX)
declare @topass as nvarchar(MAX) = ''
declare @retVal nvarchar(30)
declare @categoryid NVARCHAR(MAX)
SET @cursor = CURSOR LOCAL FOR
(select [Name] from sys.columns where object_id = (select object_id from sys.tables where name = 'ProductProperty') and [Name] like 'T%' and [name] <> 'TEMP')order by [Name] asc
--Gets column names from table from T1 -> T45
OPEN @cursor
FETCH NEXT
FROM @cursor INTO @colname
WHILE @@FETCH_STATUS = 0
BEGIN
SET @query = N'set @categoryid = (select distinct ' + @colname + ' from ProductProperty
where PartNo = @PartNo and PPFNo = @PPFno and DName = @Dimension)';
--
EXEC sp_executesql @query,
N'@PartNo nvarchar(20), @PPFno nvarchar(20),@Dimension
nvarchar(30), @categoryid NVARCHAR(MAX) OUTPUT', @PartNo, @PPFno,@Dimension,
@categoryid OUTPUT
if @categoryid is not null
begin
set @topass = @topass + @colname + '+'
end
FETCH NEXT
FROM @cursor INTO @colName
END
CLOSE @cursor
DEALLOCATE @cursor
set @topass = LEFT(@topass, LEN(@topass)-1)
set @topass = N'set @categoryid = (Select cast(('+@topass+')/'+@count+' as decimal(18,3)) from ProductProperty where PartNo = @PartNo and PPFNo = @PPFno and DName = @Dimension)';
EXEC sp_executesql @topass,
N'@PartNo nvarchar(20), @PPFno nvarchar(20),@Dimension
nvarchar(30), @categoryid NVARCHAR(MAX) OUTPUT', @PartNo, @PPFno,@Dimension,
@categoryid OUTPUT
set @topass = 'update ProductProperty set average = '+@categoryid+', Judgement = '''+@Judgement+''' where PartNo = @PartNo and PPFNo = @PPFno and DName = @Dimension';
EXEC sp_executesql @topass,
N'@PartNo nvarchar(20), @PPFno nvarchar(20),@Dimension
nvarchar(30)', @PartNo, @PPFno,@Dimension
end
这样的代码的原因
代码在VB.Net代码中每次调用时都会运行,它会从变量中获取数据到存储过程中。该代码实际上从非空的列中获取平均值
我想要什么 有没有类似
select ave(ifnull(T1.....T2,If Null dont include, ifnot null include)) from TABLE
我猜你需要这样的东西。
SELECT AVG(CASE WHEN T1 IS NOT NULL THEN T1 ELSE 0 END)
FROM TABLE
我可能会采用几种方法来重新设计您的解决方案。
如果您可以更改的内容有限,那么#2 可能是门票。但我个人的偏好是 #1 - 生成的代码更简单,如果您决定需要 T46,则无需进行任何更改。
方法编号 1:
您没有提供 table 的完整详细信息,因此我猜测您有一个构成主键的 ID
列,否则我们'我将在新解决方案中添加一个 :)
我还假设您接收 nvarchar
参数到您的存储过程是必要的而不是选择,并相应地声明数据类型。
是否无法重新设计您的数据模型?解决此问题的一种方法是将您的 "T1".."T45" 列移动到单独的 table 并将它们视为行。
您可以从 table 的:
Create Table ProductProperty
(
ID int identity,
Partno nvarchar(20),
PPFno nvarchar(20),
DName nvarchar(30),
T1 decimal(18, 3),
T2 decimal(18, 3),
T3 decimal(18, 3),
-- ...
T44 decimal(18, 3),
T45 decimal(18, 3),
average decimal(18, 3),
Judgement nvarchar(2)
)
至
Create Table ProductProperty
(
ID int identity,
Partno nvarchar(20),
PPFno nvarchar(20),
DName nvarchar(30),
average decimal(18, 3),
Judgement nvarchar(2)
) -- You would probably want to put a unique index on (Partno, PPFno, DName)
Create Table ProductPropertyT
(
ProductPropertyID int,
TNo nvarchar(2),
TValue decimal(18, 3)
)
现在您的存储过程只需要识别主键值(基于我对复合键 Partno
、PPFno
和 DName
的推测),创建或更新记录ProductPropertyT
table,并对 TValue
具有匹配 ProductPropertyId
值的记录进行简单平均:
alter procedure [dbo].[saveProdPropSampDta]
@PartNo as nvarchar(20),
@PPFno as nvarchar(20),
@Dimension as nvarchar(30),
@ToUpdate as nvarchar(10),
@count as nvarchar(2),
@Judgement as nvarchar(2)
as
begin
--remove spaces from variable @dimension
set @Dimension = REPLACE(@Dimension, ' ','')
-- determine the PK
declare @pk int
select @pk = ID from ProductProperty where Partno = @Partno and PPFno = @PPFno and DName = @Dimension
-- create / update the value in the ProductPropertyT table
if exists (select * from ProductPropertyT where ProductPropertyID = @pk and TNo = @count)
begin
update ProductPropertyT
set TValue = cast(@ToUpdate as decimal(18, 3))
where ProductPropertyID = @pk and TNo = @count
end
else
begin
insert into ProductPropertyT (ProductPropertyID, TNo, TValue)
values (@pk, @count, cast(@ToUpdate as decimal(18, 3)))
end
-- update the average and judgement in the ProductProperty table.
update ProductProperty
set average = (select avg(TValue) from ProductPropertyT where ProductPropertyID = @pk)
,Judgement = @Judgement
where ID = @pk
end
存储过程现在:更短;没有动态 sql;很容易理解。
方法编号 2:
如果您绝对必须拥有此数据模型,那么您可以使用 unpivot
从 T1...T45 列获取非空值并简单地对它们进行平均。
SELECT avg(TValue)
from
(
-- this will give you only the values for the current part/ppf/dimension
select * from ProductProperty
where Partno = @Partno and PPFno = @PPFno and DName = @Dimension
) myrow
unpivot
(
TValue
for TCol in (T1, T2, ... T44, T45)
) as x
我将把它作为练习留给您,将其编织到更新语句中 - 但足以说明您的这部分过程不需要任何动态 sql。
希望对您有所帮助。