Найти умный способ справиться с нулевым значением.Возможно ли переопределение оператора? - PullRequest
0 голосов
/ 02 июня 2019

У меня есть большая таблица формул тегов, таблица числовых данных в базе данных SQL Server 2008. Таблица формул похожа на

ID INT PRIMARY KEY, 
Formula VARCHAR(MAX), -- Nullable.

Правила:

  1. Каждый тег имеет запись в таблице формул
  2. Если формула не равна нулю, это означает, что в таблице данных нет значения
  3. Если формула пуста, это означает, что значение данных поступает из таблицы данных. В этом случае я называю это тегом 'value'.
  4. В случае 2 Формула может быть чем-то вроде T (123) + T (456), но никогда не будет как T (T (234) + T (456)). Идентификационная часть в T (ID) должна быть постоянным числом. Формула может быть + - * / () и может иметь некоторые функции SQL.

Таблица данных похожа на

DATE SMALLDATETIME PRIMARY KEY, -- 2019-06-01
ID INT PRIMARY KEY,             -- Which ID this value belongs to
VALUE FLOAT                     -- Not Nullable

Правила таблицы данных

  1. Не все теги значений имеют свои записи в таблице данных на основе даты.
  2. ДАТА и ID являются первичными ключами

Итак, я написал func ExtractTags, взял varchar (max) и вернул 2 значения необработанных тегов и извлеченную формулу.

* 1 028 * Пример:

Введите:

    'T(234) As T234, T(567) As T567'

Выход:

RawTag:
    '[1],[3],[2],[6],[8],[10],[13],[467]'
ExtractedTags:
    '(([1] + [2]) * ([3] + [6]) - [8]) As T234, ([10] + [13] + [467]) As T567'

Динамический SQL, который я сгенерировал, будет выглядеть как

SELECT DATE, (([1] + [2]) * ([3] + [6]) - [8]) As T234, ([10] + [13] + [467]) As T567
FROM (SELECT N.DATE, N.Value, N.ID 
     FROM NumericData AS N 
         Where N.DATE BETWEEN '2019-05-01' And '2019-05-3'
) 
x PIVOT (
    MAX(Value) for ID in ([1],[3],[2],[6],[8],[10],[13],[467])
) p 

Затем, основываясь на этом значении 2, я могу создать динамический SQL, который будет вращаться относительно таблицы данных, а затем получит значение и вычислит результат.

Проблема:

  • Поскольку некоторые значения могут не иметь записи на определенную дату, поэтому может отображаться значение Null. например, ([10] + [13] + [467]) ==> T567. если [467] не имеет значения, то результат равен нулю. В этом случае я хочу игнорировать значение [467] или считать его 0.
  • Если я поменяю все [xxx] на IsNull ([xxx], 0), возникнет еще одна проблема: скажем, [10], [13] и [467] равны нулю, тогда я хочу видеть T567 как ноль .
  • Я думаю создать свою собственную функцию. SUMIFNOTNULL (a, b) ==> Возвращает ноль, только если a и b равны нулю, в противном случае ноль равен 0.
  • Но проблема в том, что я не могу изменить таблицу формул, и это может продолжать расти. б) Даже если я смогу, это будет огромная работа, чтобы изменить все формулы (более 10000 записей)

Пример вывода

Если я использую IsNull ([xxx], 0), я увижу

DATE      |  T234   |  T567
----------+---------+---------
2019-05-01|  0      |  0
2019-05-02| 123.5   |  0
2019-05-03| 456.5   |  567.5

Если я не использую IsNull ([xxx], 0), я увижу

DATE      |  T234   |  T567
----------+---------+---------
2019-05-01| NULL    |  NULL
2019-05-02| 123.5   |  NULL
2019-05-03| 456.5   |  567.5

Я очень хочу увидеть

DATE      |  T234   |  T567
----------+---------+---------
2019-05-01|  0      |  NULL
2019-05-02| 123.5   |  0
2019-05-03| 456.5   |  567.5

Любая хорошая идея, чтобы решить эту проблему? Может ли SQL Server переопределить оператор '+'?

Ответы [ 2 ]

0 голосов
/ 02 июня 2019

Наконец-то я понял это. Я создаю другую функцию под названием «SmartFormula», которая будет заниматься этим. Вот код.

IF EXISTS (
    SELECT * FROM sysobjects WHERE id = object_id(N'SmartFormula') 
    AND xtype IN (N'FN', N'IF', N'TF')
)
    DROP FUNCTION SmartFormula
GO
CREATE FUNCTION SmartFormula 
(
    -- Add the parameters for the function here
    @Formula VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
    DECLARE @POS INT
    DECLARE @PEND INT
    DECLARE @RawTag VARCHAR(20)
    DECLARE @RawTags VARCHAR(MAX)
    DECLARE @RESULT VARCHAR(MAX)
    DECLARE @Field  VARCHAR(MAX)
    DECLARE @FldStart INT
    DECLARE @FldEnd INT

    SET @RESULT = ''
    SET @FldEnd = 0
    SET @FldStart = 1

    WHILE @FldStart < LEN(@Formula)
    BEGIN
        SET @FldEnd = CHARINDEX(',', @Formula, @FldStart)
        IF @FldEnd = 0 SET @FldEnd = LEN(@Formula)
        SET @Field = SUBSTRING(@Formula, @FldStart, @FldEnd - @FldStart + 1)
        SET @FldStart = @FldEnd + 1

        SET @RawTags = '';
        SET @POS = CHARINDEX('[', @Field, 1);
        WHILE @POS <> 0
        BEGIN
            SET @PEND = CHARINDEX(']', @Field, @POS);
            IF @PEND = 0 BREAK;
            SET @RawTag = SUBSTRING(@Field, @POS, @PEND - @POS + 1)
            IF CHARINDEX(@RawTag, @RawTags, 1) = 0  --Not in the tags yet
            BEGIN
                IF LEN(@RawTags) > 0
                    SET @RawTags = @RawTags + ' AND ' + @RawTag + ' Is NULL';
                ELSE
                    SET @RawTags = @RawTag + ' Is NULL';
            END
            SET @POS = CHARINDEX('[', @Field, @PEND + 1);
        END
        IF LEN(@RawTags) > 0
        BEGIN
            SEt @Field = REPLACE(@Field, 'AS', 'END AS')
            SET @Field = REPLACE(REPLACE(@Field, '[', 'ISNULL(['),']','],0)')
            SET @RESULT = @RESULT + 'CASE WHEN ' + @RawTags + ' THEN NULL ELSE ' + @Field
        END
    END
    RETURN @RESULT
END
GO

TEST

    SELECT SmartFormula('([123] + [456]) / [235] As T12,([222] - [12345]) As T222')

OUTPUT

CASE WHEN [123] Is NULL AND [456] Is NULL AND [235] Is NULL THEN NULL ELSE (ISNULL([123],0) + ISNULL([456],0)) / ISNULL([235],0) END AS T12,CASE WHEN [222] Is NULL AND [12345] Is NULL THEN NULL ELSE (ISNULL([222],0) - ISNULL([12345],0)) END AS T222
0 голосов
/ 02 июня 2019

Если я правильно понимаю, вы можете сделать это, используя выражение case:

(case when [10] is not null or [13] is not null or [467] is not null
      then coalesce([10], 0) + coalesce([13], 0) + coalesce([467], 0)
 end)

Однако я не уверен, как это вписывается в вашу структуру обработки.

Я думал, что вы можете преобразовать 0 результаты обратно в NULL:

nullif(coalesce([10], 0) + coalesce([13], 0) + coalesce([467], 0), 0)

Но, очевидно, вы можете получить допустимые нулевые значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...