Заполнение нулевых значений в таблице с использованием предыдущих ненулевых значений.Каждый столбец имеет несколько нулевых значений? - PullRequest
0 голосов
/ 18 февраля 2019

Мне нужно преобразовать таблицу A в таблицу B (то есть заполнить все нулевые значения предыдущим ненулевым значением для каждого столбца)

Основная задача - заполнить нулевые значения предыдущим ненулевымзначения для каждого столбца.

Вот исходная таблица:

FromCompany Container   Numbers     ToCompany        Location
DISCOVERY   HALU 330308   5         MAGNA CHARGE     St-Laurent
            ATSU 827944   0         LEEZA DIST. 
                          4     
COLUMBIA    CAIU 807457   3         La Cie Canada    Baie D'Urfe
                          6     
                          0     

Финальная таблица должна быть:

FromCompany Container   Numbers ToCompany       Location
DISCOVERY   HALU 330308 5       MAGNA CHARGE    St-Laurent
DISCOVERY   ATSU 827944 0       LEEZA DIST      St-Laurent
DISCOVERY   ATSU 827944 4       LEEZA DIST      St-Laurent
COLUMBIA    CAIU 807457 3       La Cie Canada   Baie D'Urfe
COLUMBIA    CAIU 807457 6       La Cie Canada   Baie D'Urfe
COLUMBIA    CAIU 807457 0       La Cie Canada   Baie D'Urfe

Помощь будет принята с благодарностью.

Ответы [ 2 ]

0 голосов
/ 18 февраля 2019

Как в значительной степени прокомментировано, вам нужен столбец, чтобы упорядочить набор данных.Поскольку ваши данные поступают из CSV-файла, вы можете, например, отредактировать файл перед загрузкой, чтобы добавить автоинкрементный номер строки.

Предполагая, что у вас есть этот столбец (id), здесьрешение SQLServer для вопроса заполнения NULL значений первым предшествующим не-NULL значением в том же столбце.

Основная идея состоит в том, чтобы поместить каждую запись в группу, номер которой соответствуетидентификатор первой записи, которая имеет ненулевое значение.Чтобы заполнить 5 столбцов, нам нужно 5 групп.

SELECT
    t.* ,
    MAX(CASE WHEN FromCompany IS NOT NULL THEN id END) OVER(ORDER BY id ROWS UNBOUNDED PRECEDING) AS grpFromCompany,
    MAX(CASE WHEN Container   IS NOT NULL THEN id END) OVER(ORDER BY id ROWS UNBOUNDED PRECEDING) AS grpContainer,
    MAX(CASE WHEN Numbers     IS NOT NULL THEN id END) OVER(ORDER BY id ROWS UNBOUNDED PRECEDING) AS grpNumbers,
    MAX(CASE WHEN ToCompany   IS NOT NULL THEN id END) OVER(ORDER BY id ROWS UNBOUNDED PRECEDING) AS grpToCompany,
    MAX(CASE WHEN Location    IS NOT NULL THEN id END) OVER(ORDER BY id ROWS UNBOUNDED PRECEDING) AS grpLocation
FROM mytable t

Возвращает:

id | FromCompany | Container   | Numbers | ToCompany     | Location    | grpFromCompany | grpContainer | grpNumbers | grpToCompany | grpLocation
-: | :---------- | :---------- | ------: | :------------ | :---------- | -------------: | -----------: | ---------: | -----------: | ----------:
 1 | DISCOVERY   | HALU 330308 |       5 | MAGNA CHARGE  | St-Laurent  |              1 |            1 |          1 |            1 |           1
 2 | <em>null</em>        | ATSU 827944 |       0 | LEEZA DIST.   | <em>null</em>        |              1 |            2 |          2 |            2 |           1
 3 | <em>null</em>        | <em>null</em>        |       4 | <em>null</em>          | <em>null</em>        |              1 |            2 |          3 |            2 |           1
 4 | COLUMBIA    | CAIU 807457 |       3 | La Cie Canada | Baie D'Urfe |              4 |            4 |          4 |            4 |           4
 5 | <em>null</em>        | <em>null</em>        |       6 | <em>null</em>          | <em>null</em>        |              4 |            4 |          5 |            4 |           4
 6 | <em>null</em>        | <em>null</em>        |       0 | <em>null</em>          | <em>null</em>        |              4 |            4 |          6 |            4 |           4

Теперь мы можем превратить это в CTE и использовать его для поиска соответствующих значений втаблица:

WITH mycte AS (
    SELECT
        t.* ,
        MAX(CASE WHEN FromCompany IS NOT NULL THEN id END) OVER(ORDER BY id ROWS UNBOUNDED PRECEDING) AS grpFromCompany,
        MAX(CASE WHEN Container   IS NOT NULL THEN id END) OVER(ORDER BY id ROWS UNBOUNDED PRECEDING) AS grpContainer,
        MAX(CASE WHEN Numbers     IS NOT NULL THEN id END) OVER(ORDER BY id ROWS UNBOUNDED PRECEDING) AS grpNumbers,
        MAX(CASE WHEN ToCompany   IS NOT NULL THEN id END) OVER(ORDER BY id ROWS UNBOUNDED PRECEDING) AS grpToCompany,
        MAX(CASE WHEN Location    IS NOT NULL THEN id END) OVER(ORDER BY id ROWS UNBOUNDED PRECEDING) AS grpLocation
    FROM mytable t
)
SELECT 
   id,
   (SELECT FromCompany FROM mytable WHERE id = grpFromCompany) AS FromCompany,
   (SELECT Container   FROM mytable WHERE id = grpFromCompany) AS Container,
   (SELECT Numbers     FROM mytable WHERE id = grpNumbers) AS Numbers,
   (SELECT ToCompany   FROM mytable WHERE id = grpToCompany) AS ToCompany,
   (SELECT Location    FROM mytable WHERE id = grpLocation) AS Location
FROM mycte 

GO
id | FromCompany | Container   | Numbers | ToCompany     | Location   
-: | :---------- | :---------- | ------: | :------------ | :----------
 1 | DISCOVERY   | HALU 330308 |       5 | MAGNA CHARGE  | St-Laurent 
 2 | DISCOVERY   | HALU 330308 |       0 | LEEZA DIST.   | St-Laurent 
 3 | DISCOVERY   | HALU 330308 |       4 | LEEZA DIST.   | St-Laurent 
 4 | COLUMBIA    | CAIU 807457 |       3 | La Cie Canada | Baie D'Urfe
 5 | COLUMBIA    | CAIU 807457 |       6 | La Cie Canada | Baie D'Urfe
 6 | COLUMBIA    | CAIU 807457 |       0 | La Cie Canada | Baie D'Urfe

дБ <> скрипка здесь

0 голосов
/ 18 февраля 2019

Обычно, если в вашей таблице есть столбец идентификаторов или способ гарантировать сортировку строк, вы можете использовать CTE для достижения этого с относительной эффективностью.Однако у нас нет такой роскоши, поэтому другое решение - использовать гораздо менее эффективный CURSOR.

-- Cursor variables
DECLARE @FromCompanyCursor varchar(20),
        @ContainerCursor varchar(20),
        @NumbersCursor int,
        @ToCompanyCursor varchar(20),
        @LocationCursor varchar(20),
        @FromCompany varchar(20),
        @Container varchar(20),
        @Numbers int,
        @ToCompany varchar(20),
        @Location varchar(20);


-- Cursor declaration
DECLARE C CURSOR FOR
(
SELECT  FromCompany,
        Container,
        Numbers,
        ToCompany,
        Location
FROM TableName
)
FOR UPDATE OF FromCompany, Container, Numbers, ToCompany, Location;

OPEN C;

-- Get first row from the cursor
FETCH NEXT FROM C INTO @FromCompanyCursor, @ContainerCursor, @NumbersCursor, @ToCompanyCursor, @LocationCursor;

-- While we still have rows to iterate over
WHILE @@FETCH_STATUS = 0
BEGIN
    -- Keep track of the last non-null value
    SELECT @FromCompany = CASE WHEN @FromCompanyCursor IS NOT NULL THEN @FromCompanyCursor ELSE @FromCompany END,
           @Container = CASE WHEN @ContainerCursor IS NOT NULL THEN @ContainerCursor ELSE @Container END,
           @Numbers = CASE WHEN @NumbersCursor IS NOT NULL THEN @NumbersCursor ELSE @Numbers END,
           @ToCompany = CASE WHEN @ToCompanyCursor IS NOT NULL THEN @ToCompanyCursor ELSE @ToCompany END,
           @Location = CASE WHEN @LocationCursor IS NOT NULL THEN @LocationCursor ELSE @Location END;

    -- Update the table with the last non-null values
    UPDATE TableName
    SET FromCompany = @FromCompany,
        Container = @Container,
        Numbers = @Numbers,
        ToCompany = @ToCompany,
        Location = @Location
    WHERE CURRENT OF C;

    -- Get the next row from the cursor
    FETCH NEXT FROM C INTO @FromCompanyCursor, @ContainerCursor, @NumbersCursor, @ToCompanyCursor, @LocationCursor;
END

-- Don't forget to close the cursor!
CLOSE C;
DEALLOCATE C;

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

...