Есть ли способ получить все ненулевое значение из группы столбцов в SQL? - PullRequest
0 голосов
/ 19 сентября 2018

Добрый день,

Я хотел бы спросить, есть ли более простой способ получить группу столбцов, которые не равны нулю?

Сценарий

У меня есть загрузчик, который получает данные из 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 голосов
/ 19 сентября 2018

Есть несколько способов, которыми я мог бы переработать ваше решение.

Если вы ограничены в том, что вы можете изменить, тогда № 2 может быть билетом.Но мое личное предпочтение было бы № 1 - полученный код проще, и нет никаких изменений, если вы решите, что вам нужен T46.

Подход № 1:

Вы не предоставили полную информацию о своих таблицах, поэтому я предполагаю, что у вас есть столбец ID, который составляет первичный ключ, или, если нет, мы добавим его в новое решение:)

Я также собираюсь предположить, что вы получаете nvarchar параметры в свой сохраненный процесс по необходимости, а не по выбору, и соответственно объявляете типы данных.

Это редизайн вашей модели данных извопроса?Один из способов сделать это - переместить столбцы "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)
)

Теперь вашей хранимой процедуре просто необходимо определить значение первичного ключа (основываясь на предположении, чтосоставной ключ Partno, PPFno и DName), создайте или обновите запись в таблице ProductPropertyT и выполните простое среднее значение по 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 для этой части вашей процедуры.

Надеюсь, это поможет.

0 голосов
/ 19 сентября 2018

Полагаю, вам нужно что-то подобное.

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