有没有办法从SQL的一组列中获取所有非null值?

时间:2018-09-19 07:51:22

标签: sql tsql stored-procedures sql-server-2008-r2 cursor

早安,

我想问一下,有没有更简单的方法来获取一组非空的列?

场景

我有一个可从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

2 个答案:

答案 0 :(得分:1)

我可能有几种方法可以重新设计您的解决方案。

如果您只能进行更改,则#2可能是门票。但是我个人的偏好是#1-生成的代码更简单,并且如果您决定需要T46,则无需进行任何更改。

方法1:

您尚未提供表的完整详细信息,因此我猜您有一个构成主键的ID列,否则,我们将在新解决方案中添加一个:)

我还要假设您是根据需要而不是通过选择将nvarchar参数接收到存储的proc中,并相应地声明数据类型。

是否完全可以重新设计数据模型?一种解决方法是将“ T1” ..“ T45”列移到单独的表中,并将它们视为行。

您可以从以下表格中找到

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)
)

现在,您的存储过程只需要标识主键值(根据我对复合键PartnoPPFnoDName的推测),即可在{中创建或更新一条记录{1}}表,并对ProductPropertyT值匹配的记录进行TValue的简单平均:

ProductPropertyId

现在存储的proc是:更短;没有动态SQL;很容易理解。

方法2:

如果您绝对必须具有此数据模型,则可以使用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 从T1 ... T45列中获取非空值,然后对它们进行平均。

unpivot

我会将其保留为练习,以将其织入一条update语句中-但足以说明您在过程的这一部分不需要任何动态sql。

希望有帮助。

答案 1 :(得分:0)

我想你需要这样的东西。

SELECT AVG(CASE WHEN T1 IS NOT NULL THEN T1 ELSE 0 END)
FROM TABLE