0

我在 SQL Server 2008 中有一个包含大约 50 列的表。这些列中的每一列都对应一个条形码扫描仪;我们在工厂周围散布着各种串口转 IP 盒。收到扫描后,一个小型应用程序会在相关字段中插入一个日期。

我要做的是对此表中的字段进行比较,而不必手动列出它们;理想情况下,我希望能够从表中添加/删除字段,而不必更改在表上运行的 SPROCS/脚本。

所以而不是

UPDATE scans 
SET completed = 1 
WHERE (scanner_1 IS NOT NULL OR scanner_2 IS NOT NULL)

...我正在尝试一些类似的东西

UPDATE scans 
SET completed = 1 
WHERE (/* Something to select all columns, maybe with an exclusion */ WHERE fields IS NOT NULL)

我希望我已经解释得很好,感谢大家的关注!


我们有一张表格,列出了所有的扫描仪和 COM 端口,例如

id (PK)
com_port
scanner

然后是每个扫描仪都有一个字段的表,键是创建记录时自动插入的条形码编号(用作真实名称的示例名称有点神秘)

barcode_id (PK)
scanner_1
scanner_2
scanner_3
4

1 回答 1

1

我当然同意有关规范化数据库的评论。理想情况下,您应该有一个单独的表,其中包含链接回 IP 盒和扫描仪的外键。

为了帮助规范化数据,您可能需要查看该UNPIVOT函数。例如

CREATE TABLE T (ID INT NOT NULL, Scanner1 DATETIME, Scanner2 DATETIME, Scanner3 DATETIME, Scanner4 DATETIME)

INSERT INTO T VALUES 
    (1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
    (2, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL),
    (3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL, NULL),
    (4, CURRENT_TIMESTAMP, NULL, NULL, NULL),
    (5, NULL, NULL, NULL, NULL)

SELECT  ID, Scanner, ScannedDate
FROM    T
        UNPIVOT 
        (   ScannedDate 
            FOR Scanner IN ([Scanner1], [Scanner2], [Scanner3], [Scanner4])
        ) UnPvt
ORDER BY ID, Scanner

这将返回如下内容:

ID  Scanner     ScannedDate
-----------------------------------
1   Scanner1    May 31 2012 10:40AM
1   Scanner2    May 31 2012 10:40AM
1   Scanner3    May 31 2012 10:40AM
1   Scanner4    May 31 2012 10:40AM
2   Scanner1    May 31 2012 10:40AM
2   Scanner2    May 31 2012 10:40AM
2   Scanner3    May 31 2012 10:40AM
3   Scanner1    May 31 2012 10:40AM
3   Scanner2    May 31 2012 10:40AM
4   Scanner1    May 31 2012 10:40AM

这仍然存在必须手动写出列的问题,但是您也可以动态创建 UNPIVOT 语句:

DECLARE @SQL NVARCHAR(MAX) = '',
        @Columns NVARCHAR(MAX) = ''

SELECT  @Columns = @Columns + ',' + QUOTENAME(Name)
FROM    SYS.COLUMNS
WHERE   OBJECT_ID = OBJECT_ID(N'T')
AND     LEFT(Name, 7) = 'Scanner'

SET @SQL = 'SELECT  ID, Scanner, ScannedDate
            FROM    T
                    UNPIVOT 
                    (   ScannedDate 
                        FOR Scanner IN (' + STUFF(@Columns, 1, 1, '') + ')
                    ) UnPvt
            ORDER BY ID, Scanner'

DECLARE @UnPivot TABLE (ID INT, Scanner VARCHAR(50), ScannedDate DATETIME)
INSERT INTO @UnPivot
EXECUTE SP_EXECUTESQL @SQL

SELECT  *
FROM    @UnPivot

最后,如果您需要将标准化数据转换回平面视图,这仍然可以使用该PIVOT函数完成(假设 @SQL 和 @Columns 已经如上定义):

CREATE TABLE ##UnPivot (ID INT, Scanner VARCHAR(50), ScannedDate DATETIME)
INSERT INTO ##UnPivot
EXECUTE SP_EXECUTESQL @SQL

SELECT  *
FROM    ##UnPivot

SET @SQL = 'SELECT  ID' + @Columns + '
            FROM    ##Unpivot
                    PIVOT 
                    (   MAX(ScannedDate)
                        FOR Scanner IN (' + STUFF(@Columns, 1, 1, '') + ')
                    ) UnPvt
            ORDER BY ID'

EXECUTE SP_EXECUTESQL @SQL

DROP TABLE ##UnPivot

我已经放置了一个完整的动态反透视数据的工作示例,然后在SQL Fiddle上再次对其进行透视


编辑

我刚刚意识到我实际上并没有回答最初的问题,我只是建议如何规范化数据。以下是无需手动输入列的方法:

DECLARE @SQL NVARCHAR(MAX) = '',
        @Columns NVARCHAR(MAX) = ' WHERE 1 = 1 '

SELECT  @Columns = @Columns + 'AND ' + QUOTENAME(Name) + ' IS NOT NULL '
FROM    SYS.COLUMNS
WHERE   OBJECT_ID = OBJECT_ID(N'T')
AND     LEFT(Name, 7) = 'Scanner'

SET @SQL = 'UPDATE T SET Completed = 1' + @Columns
EXECUTE SP_EXECUTESQL @SQL

同样,在SQL Fiddle上有一个工作示例

于 2012-05-31T09:56:57.467 回答