简体   繁体   English

使用以前的非空值填充表中的空值。 每列有几个空值?

[英]Filling null values in the table using previous non null values. Each column has few null values?

I need to convert Table A to Table B (That is fill all the null values with the previous non null value for each column)我需要将表 A 转换为表 B(即用每列的前一个非空值填充所有空值)

The main task is to fill the null values with the previous non null values for each column.主要任务是用每列之前的非空值填充空值。

Here is the Original Table:这是原始表:

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     

The Final Table should be:决赛桌应该是:

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

Help would be greatly appreciated.帮助将不胜感激。

As largely commented, you do need a column to order the dataset.正如大量评论的那样,您确实需要一列来对数据集进行排序。 As your data comes from a CSV file, you can, for example, edit the file before loading it to add an autoincremented row number.由于您的数据来自 CSV 文件,例如,您可以在加载文件之前编辑该文件以添加自动递增的行号。

Assuming that you have this column ( id ) in place, here is a SQLServer solution for the question of filling NULL values with the first preceeding non- NULL value in the same column.假设你有这个列 ( id ),这里是一个 SQLServer 解决方案,用于用同一列中的第一个非NULL值填充NULL值的问题。

The basic idea is to put each record into a group, whose number corresponds to the id of the first record that has a non-null value.基本思想是将每条记录放入一个组中,其编号对应于第一条具有非空值的记录的id。 With 5 columns to fill, we need 5 groups.要填充 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

Returns :回报:

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 | null        | ATSU 827944 |       0 | LEEZA DIST.   | null        |              1 |            2 |          2 |            2 |           1
 3 | null        | null        |       4 | null          | null        |              1 |            2 |          3 |            2 |           1
 4 | COLUMBIA    | CAIU 807457 |       3 | La Cie Canada | Baie D'Urfe |              4 |            4 |          4 |            4 |           4
 5 | null        | null        |       6 | null          | null        |              4 |            4 |          5 |            4 |           4
 6 | null        | null        |       0 | null          | null        |              4 |            4 |          6 |            4 |           4

Now we can turn this into a CTE, and use it to lookup the relevant values in the table :现在我们可以把它变成一个 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

db<>fiddle here db<> 在这里摆弄

Normally if your table had an identity column or a way to guarantee row sorting, you could use a CTE to achieve this with relative efficiency.通常,如果您的表具有标识列或保证行排序的方法,您可以使用 CTE 以相对高效的方式实现这一点。 However, we don't have that luxury here, so another solution is to use a much less efficient CURSOR instead.然而,我们在这里没有那么奢侈,所以另一种解决方案是使用效率低得多的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;

Note that procedural-based operations like these are incredibly inefficient in SQL Server, and as such a solution such as this one should be used as a one-time operation, or as part of a scheduled maintenance job.请注意,像这样基于过程的操作在 SQL Server 中的效率非常低,因此像这样的解决方案应该用作一次性操作,或作为计划维护作业的一部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM