【问题标题】:How to create a query on an existing table and build a table(view) with bool data using a logic condition?如何使用逻辑条件在现有表上创建查询并使用布尔数据构建表(视图)?
【发布时间】:2015-09-09 07:38:28
【问题描述】:

我拥有的是一个 MS-SQL 数据库,用于存储来自某些车辆上安装的设备的数据/信息(每辆车 1-3 个设备)。

目前,数据库中有一个名为Communication 的表——一个大表,用于存储设备连接到 TCP 服务器时的所有信息。记录一个接一个地添加(这里只插入)。

表格如下所示:

我需要的是一个SQL 查询(命令/语句)来为所谓的“每周通信状态”创建一个表(视图),在那里我可以看到 车辆是否/如何进行通信过去 7 天...类似于下表:

【问题讨论】:

    标签: sql sql-server pivot conditional-statements pivot-table


    【解决方案1】:

    作为上述的替代方案,假设您知道输出中只有 7 个日期列,您可以进行手动透视而不是使用 PIVOT 运算符。

    See SQLFiddle

    select VehicleNumber, 
          CASE WHEN SUM(CASE WHEN DATEDIFF(d,DateTimeCommunication, DATEADD(d,-7,GETDATE())) = 0 THEN 1 ELSE 0 END) > 0 THEN 'YES' Else 'NO' END AS [7 Days Ago]
          ,CASE WHEN SUM(CASE WHEN DATEDIFF(d,DateTimeCommunication, DATEADD(d,-6,GETDATE())) = 0 THEN 1 ELSE 0 END) > 0 THEN 'YES' Else 'NO' END AS [6 Days Ago]
          ,CASE WHEN SUM(CASE WHEN DATEDIFF(d,DateTimeCommunication, DATEADD(d,-5,GETDATE())) = 0 THEN 1 ELSE 0 END) > 0 THEN 'YES' Else 'NO' END AS [5 Days Ago]
          ,CASE WHEN SUM(CASE WHEN DATEDIFF(d,DateTimeCommunication, DATEADD(d,-4,GETDATE())) = 0 THEN 1 ELSE 0 END) > 0 THEN 'YES' Else 'NO' END AS [4 Days Ago]
          ,CASE WHEN SUM(CASE WHEN DATEDIFF(d,DateTimeCommunication, DATEADD(d,-3,GETDATE())) = 0 THEN 1 ELSE 0 END) > 0 THEN 'YES' Else 'NO' END AS [3 Days Ago]
          ,CASE WHEN SUM(CASE WHEN DATEDIFF(d,DateTimeCommunication, DATEADD(d,-2,GETDATE())) = 0 THEN 1 ELSE 0 END) > 0 THEN 'YES' Else 'NO' END AS [2 Days Ago]
          ,CASE WHEN SUM(CASE WHEN DATEDIFF(d,DateTimeCommunication, DATEADD(d,-1,GETDATE())) = 0 THEN 1 ELSE 0 END) > 0 THEN 'YES' Else 'NO' END AS [1 Days Ago]
          ,CASE WHEN SUM(CASE WHEN DATEDIFF(d,DateTimeCommunication, GETDATE()) = 0 THEN 1 ELSE 0 END) > 0 THEN 'YES' Else 'NO' END AS [0 Days Ago]
    from Communications
    GROUP BY VehicleNumber
    

    【讨论】:

    • @groenhen 你会注意到 Sum 函数包裹了每一行。因为我们按车辆编号 I.E. 对数据进行分组。每行显示一辆车,显示的所有其他数据都是汇总的。在这种情况下,聚合是为单个车辆获取行并将它们挤压在一起。然后,每次我们有匹配的日期时,sum 函数都会有效地计数。因此,在行尾我们会说,如果这个计数 > 0 则 'Yes' 。因此,由于这是使用 group by,我们需要使用聚合函数,因此我们需要做所有额外的事情。在我看来更容易阅读。好吗?
    • 谢谢沙林。问完这个问题后我立刻想通了,可能是我太累了,因为我在第一次阅读时没有注意到 SUM ......这就是我删除评论的原因。但是非常欢迎您的回答,因为得到确认总是好的:)
    • @groenhen 没问题。如果有帮助,请随时将其标记为正确并点赞!
    • 推迟到明天,因为我的车今天早上顽固地拒绝启动。现在已修复,所以明天我会回到正轨:) 嘿——跑题了,但非常有用——现在我知道如何更换汽车的燃油滤清器,而不必祈祷它在更换后启动! ;)
    • 忘了说:感谢 SQL Fiddle!不知道这样的东西存在......很好!
    【解决方案2】:

    这是一个动态的PIVOT 方法。

    CREATE TABLE #temp(equipmentID int, vehicleNumber int, DateTimeCommunication datetime)
    
    INSERT INTO #temp(equipmentID, vehicleNumber, DateTimeCommunication)
    VALUES  (1,100,GETDATE()),
            (2,110,GETDATE()),
            (3,120,GETDATE()),
            (5,140,GETDATE()),
            (1,100,DATEADD(day,-8,GETDATE())),
            (3,120,DATEADD(day,-8,GETDATE())),
            (4,130,DATEADD(day,-8,GETDATE())),
            (5,140,DATEADD(day,-8,GETDATE()))
    
    DECLARE @sql nvarchar(max), @columns nvarchar(max), @columnsSelect nvarchar(max)
    
    SELECT @columns = COALESCE(@columns + N',['+CONVERT(nvarchar(max),dateCom)+N']',N'['+CONVERT(nvarchar(max),dateCom)+N']')
    FROM (
        SELECT DISTINCT CONVERT(date, DateTimeCommunication) as dateCom
        FROM #temp
    ) as allDates
    ORDER BY dateCom
    
    SELECT @columnsSelect = COALESCE(@columnsSelect + N',ISNULL(['+CONVERT(nvarchar(max),dateCom)+N'],N''NO'') as '''+CONVERT(nvarchar(max),dateCom)+'''',
                                        N'ISNULL(['+CONVERT(nvarchar(max),dateCom)+N'],N''NO'') as '''+CONVERT(nvarchar(max),dateCom)+'''')
    FROM (
        SELECT DISTINCT CONVERT(date, DateTimeCommunication) as dateCom
        FROM #temp
    ) as allDates
    ORDER BY dateCom
    
    SET @sql = N'SELECT pvt.vehicleNumber, '+@columnsSelect+'
    FROM (
        SELECT t.equipmentID, t.vehicleNumber, CONVERT(date,DateTimeCommunication) as dateCom,
            CASE WHEN t.DateTimeCommunication BETWEEN DATEADD(day,-7,GETDATE()) AND GETDATE() 
                THEN N''YES''
                ELSE N''NO''
            END as communicated
        FROM #temp t
    ) as dat
    PIVOT(
        MAX(communicated)
        FOR dateCom IN('+@columns+')
    ) as pvt'
    PRINT(@sql)
    EXEC (@sql)
    
    DROP TABLE #temp
    

    如果您不需要它是动态的或仅用于指定的日期范围,只需将其再次简化为:

    CREATE TABLE #temp(equipmentID int, vehicleNumber int, DateTimeCommunication datetime)
    
    INSERT INTO #temp(equipmentID, vehicleNumber, DateTimeCommunication)
    VALUES  (1,100,GETDATE()),
            (2,110,GETDATE()),
            (3,120,GETDATE()),
            (5,140,GETDATE()),
            (1,100,DATEADD(day,-8,GETDATE())),
            (3,120,DATEADD(day,-8,GETDATE())),
            (4,130,DATEADD(day,-8,GETDATE())),
            (5,140,DATEADD(day,-8,GETDATE()))
    
    SELECT *
    FROM (
        SELECT t.equipmentID, t.vehicleNumber, CONVERT(date,DateTimeCommunication) as dateCom,
            CASE WHEN t.DateTimeCommunication BETWEEN DATEADD(day,-7,GETDATE()) AND GETDATE() 
                THEN N'YES'
                ELSE N'NO'
            END as communicated
        FROM #temp t
    ) as dat
    PIVOT(
        MAX(communicated)
        FOR dateCom IN([2015-06-23]) --needs to be changed on different date!
    ) as pvt
    
    DROP TABLE #temp
    

    【讨论】:

    • 我明天一定会尝试第二种方法。现在/这里我没有 SQL 服务器,所以... :) 非常感谢,它会工作 :)
    • 没问题。告诉我它是否不是你要找的东西。但一切都在我的机器上工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-19
    • 1970-01-01
    • 2020-06-23
    相关资源
    最近更新 更多