如何构造查询以更改sql表

时间:2016-08-12 12:09:49

标签: sql sql-server sql-server-2008 tsql sql-server-2005

我需要一点帮助......所以,让我向你介绍我的问题。

我有以下SQL Server表:

| RankCode | SeaPortInd | WatchKeepingInd |      EffectiveDate      | VesselCode |        FromDate         |         ToDate          |        LastDate         | LastUser |
+----------+------------+-----------------+-------------------------+------------+-------------------------+-------------------------+-------------------------+----------+
| C/E      |          0 |               0 | 1900-01-01 00:00:00.000 |        031 | 1900-01-01 05:00:00.000 | 1900-01-01 07:00:00.000 | 2016-08-11 12:40:00.000 | d.baltas |
| C/E      |          0 |               0 | 2016-06-02 00:00:00.000 |        031 | 1900-01-01 00:00:00.000 | 1900-01-01 00:00:00.000 | 1900-01-01 00:00:00.000 | d.baltas |
| C/E      |          0 |               1 | 2016-06-01 00:00:00.000 |        031 | 1900-01-01 01:00:00.000 | 1900-01-01 02:00:00.000 | 2016-08-11 17:58:00.000 | d.baltas |
| C/E      |          0 |               1 | 2016-06-02 00:00:00.000 |        031 | 1900-01-01 01:00:00.000 | 1900-01-01 02:00:00.000 | 2016-08-10 17:58:00.000 | d.baltas |
| C/E      |          1 |               1 | 2016-06-01 00:00:00.000 |        031 | 1900-01-01 03:00:00.000 | 1900-01-01 04:00:00.000 | 2016-08-10 17:58:00.000 | d.baltas |
| MSTR     |          0 |               0 | 2016-06-02 00:00:00.000 |        031 | 1900-01-01 16:00:00.000 | 1900-01-01 22:00:00.000 | 2016-08-10 17:58:00.000 | d.baltas |
| MSTR     |          0 |               1 | 2016-06-01 00:00:00.000 |        031 | 1900-01-01 08:00:00.000 | 1900-01-01 12:00:00.000 | 2016-08-10 17:58:00.000 | d.baltas |
| MSTR     |          1 |               0 | 2016-06-03 00:00:00.000 |        031 | 1900-01-01 08:00:00.000 | 1900-01-01 14:00:00.000 | 2016-08-11 15:00:00.000 | d.baltas |
+----------+------------+-----------------+-------------------------+------------+-------------------------+-------------------------+-------------------------+----------+

我想获取类似于此表的输出:

enter image description here

表格的更多解释:

海上预定的每日工作时间表示SeaPortInd = 1

港口的预定每日工作时间表示SeaPortInd = 0

值守是指WatchkeepingInd = 1

NonWatchkeeping表示WatchkeepingInd = 0

我设法进入下表:

+----------+--------------------+
| RankCode | SeaNonWatchkeeping |
| C/E      |  00:00 - 00:00     |
|          |  05:00 - 07:00     |
| MSTR     |  16:00 - 22:00     |
+----------+--------------------+

查询:

SELECT CASE 
        WHEN row_number() OVER (
                PARTITION BY RankCode ORDER BY FromDate asc
    ) = 1
            THEN RankCode
        ELSE ''
        END AS RankCode

    ,substring(convert(VARCHAR(255), FromDate, 120), 11, 6) + ' -' + substring(convert(VARCHAR(255), ToDate, 120), 11, 6) AS SeaNonWatchkeeping

FROM WorkingHoursSchedule WHERE SeaPortInd = 0 AND watchkeepingind = 0

你能帮我解决一下SeaportInd = 0和Watchkeeping = 1等问题吗?

我正在使用SQL Server 2008,但查询也将在一些以前最低版本的SQL Server 2005上运行

提前致谢!

3 个答案:

答案 0 :(得分:1)

这个应该有效:

WITH WorkingHoursSchedule AS
(
    SELECT * FROM (VALUES
    ('C/E ', 0, 0, '1900-01-01 00:00:00.000', '031', '1900-01-01 05:00:00.000', '1900-01-01 07:00:00.000', '2016-08-11 12:40:00.000', 'd.baltas'),
    ('C/E ', 0, 0, '2016-06-02 00:00:00.000', '031', '1900-01-01 00:00:00.000', '1900-01-01 00:00:00.000', '1900-01-01 00:00:00.000', 'd.baltas'),
    ('C/E ', 0, 1, '2016-06-01 00:00:00.000', '031', '1900-01-01 01:00:00.000', '1900-01-01 02:00:00.000', '2016-08-11 17:58:00.000', 'd.baltas'),
    ('C/E ', 0, 1, '2016-06-02 00:00:00.000', '031', '1900-01-01 01:00:00.000', '1900-01-01 02:00:00.000', '2016-08-10 17:58:00.000', 'd.baltas'),
    ('C/E ', 1, 1, '2016-06-01 00:00:00.000', '031', '1900-01-01 03:00:00.000', '1900-01-01 04:00:00.000', '2016-08-10 17:58:00.000', 'd.baltas'),
    ('MSTR', 0, 0, '2016-06-02 00:00:00.000', '031', '1900-01-01 16:00:00.000', '1900-01-01 22:00:00.000', '2016-08-10 17:58:00.000', 'd.baltas'),
    ('MSTR', 0, 1, '2016-06-01 00:00:00.000', '031', '1900-01-01 08:00:00.000', '1900-01-01 12:00:00.000', '2016-08-10 17:58:00.000', 'd.baltas'),
    ('MSTR', 1, 0, '2016-06-03 00:00:00.000', '031', '1900-01-01 08:00:00.000', '1900-01-01 14:00:00.000', '2016-08-11 15:00:00.000', 'd.baltas')
    )T(RankCode, SeaPortInd, WatchKeepingInd, EffectiveDate, VesselCode, FromDate, ToDate, LastDate, LastUser)
), Src AS
(
    SELECT ROW_NUMBER() OVER (PARTITION BY RankCode ORDER BY RankCode, SeaPortInd, WatchKeepingInd) RN, RankCode
    FROM WorkingHoursSchedule
), SeaWatchKeeping AS
(
    SELECT ROW_NUMBER() OVER (PARTITION BY RankCode ORDER BY FromDate) RN, RankCode,
        SUBSTRING(CONVERT(VARCHAR(255), FromDate, 120), 12, 5) + ' - ' + SUBSTRING(CONVERT(VARCHAR(255), ToDate, 120), 12, 5) SeaWatchKeeping
    FROM WorkingHoursSchedule
    WHERE SeaPortInd = 0 AND WatchKeepingInd = 1
), SeaNonWatchKeeping AS
(
    SELECT ROW_NUMBER() OVER (PARTITION BY RankCode ORDER BY FromDate) RN, RankCode,
        SUBSTRING(CONVERT(VARCHAR(255), FromDate, 120), 12, 5) + ' - ' + SUBSTRING(CONVERT(VARCHAR(255), ToDate, 120), 12, 5) SeaNonWatchKeeping
    FROM WorkingHoursSchedule
    WHERE SeaPortInd = 0 AND WatchKeepingInd = 0
), LandWatchKeeping AS
(
    SELECT ROW_NUMBER() OVER (PARTITION BY RankCode ORDER BY FromDate) RN, RankCode,
        SUBSTRING(CONVERT(VARCHAR(255), FromDate, 120), 12, 5) + ' - ' + SUBSTRING(CONVERT(VARCHAR(255), ToDate, 120), 12, 5) LandWatchKeeping
    FROM WorkingHoursSchedule
    WHERE SeaPortInd = 1 AND WatchKeepingInd = 1
), LandNonWatchKeeping AS
(
    SELECT ROW_NUMBER() OVER (PARTITION BY RankCode ORDER BY FromDate) RN, RankCode,
        SUBSTRING(CONVERT(VARCHAR(255), FromDate, 120), 12, 5) + ' - ' + SUBSTRING(CONVERT(VARCHAR(255), ToDate, 120), 12, 5) LandNonWatchKeeping
    FROM WorkingHoursSchedule
    WHERE SeaPortInd = 1 AND WatchKeepingInd = 0
)
SELECT CASE WHEN S.RN=1 THEN S.RankCode ELSE NULL END RankCode, SeaWatchKeeping, SeaNonWatchKeeping, LandWatchKeeping, LandNonWatchKeeping
FROM Src S
LEFT JOIN SeaNonWatchKeeping SN ON S.RN=SN.RN AND S.RankCode=SN.RankCode
LEFT JOIN SeaWatchKeeping SW ON S.RN=SW.RN AND S.RankCode=SW.RankCode
LEFT JOIN LandNonWatchKeeping LN ON S.RN=LN.RN AND S.RankCode=LN.RankCode
LEFT JOIN LandWatchKeeping LW ON S.RN=LW.RN AND S.RankCode=LW.RankCode
WHERE SeaWatchKeeping IS NOT NULL OR SeaNonWatchKeeping IS NOT NULL OR LandWatchKeeping IS NOT NULL OR LandNonWatchKeeping IS NOT NULL

行根据RankCode占用的空间折叠,并按FromDate排序:

RankCode SeaWatchKeeping SeaNonWatchKeeping LandWatchKeeping LandNonWatchKeeping
-------- --------------- ------------------ ---------------- -------------------
C/E      01:00 - 02:00   00:00 - 00:00      03:00 - 04:00    NULL
NULL     01:00 - 02:00   05:00 - 07:00      NULL             NULL
MSTR     08:00 - 12:00   16:00 - 22:00      NULL             08:00 - 14:00

答案 1 :(得分:0)

以下SQL使用CASE表达式根据您的要求进行评估。

SELECT  [RankCode]
    ,   CASE
            WHEN [SeaPortInd] = 1 AND [WatchKeepingInd] = 1 THEN substring(convert(VARCHAR(255), [FromDate], 120), 11, 6) + ' -' + substring(convert(VARCHAR(255), [ToDate], 120), 11, 6)
        END as 'At Sea: Watchkeeping (from...to)'
    ,   CASE
            WHEN [SeaPortInd] = 1 AND [WatchKeepingInd] = 0 THEN substring(convert(VARCHAR(255), [FromDate], 120), 11, 6) + ' -' + substring(convert(VARCHAR(255), [ToDate], 120), 11, 6)
        END as 'At Sea: Non-Watchkeeping duties (from...to)'    
    ,   CASE
            WHEN [SeaPortInd] = 0 AND [WatchKeepingInd] = 1 THEN substring(convert(VARCHAR(255), [FromDate], 120), 11, 6) + ' -' + substring(convert(VARCHAR(255), [ToDate], 120), 11, 6)
        END as 'In Port: Watchkeeping (from...to)'
    ,   CASE
            WHEN [SeaPortInd] = 0 AND [WatchKeepingInd] = 0 THEN substring(convert(VARCHAR(255), [FromDate], 120), 11, 6) + ' -' + substring(convert(VARCHAR(255), [ToDate], 120), 11, 6)
        END as 'In Port: Non-Watchkeeping duties (from...to)'   
  FROM [dbo].[WorkingHoursSchedule]

结果如下: enter image description here

答案 2 :(得分:0)

我认为每艘船都有自己的时间表,允许一次检索多艘船只。此解决方案使用计数表来创建插槽。

--original data
declare @WorkingHoursSchedule table (
        Ident           int identity(1,1),
        RankCode        varchar(5),
        SeaPortInd      bit,
        WatchKeepingInd bit,
        EffectiveDate   datetime,
        VesselCode      varchar(5),
        FromDate        datetime,
        ToDate          datetime,
        LastDate        datetime,
        LastUser        varchar(128));

insert @WorkingHoursSchedule values
('C/E',0,0,'1900-01-01 00:00:00.000','031','1900-01-01 05:00:00.000','1900-01-01 07:00:00.000','2016-08-11 12:40:00.000','d.baltas'),
('C/E',0,0,'2016-06-02 00:00:00.000','031','1900-01-01 00:00:00.000','1900-01-01 00:00:00.000','1900-01-01 00:00:00.000','d.baltas'),
('C/E',0,1,'2016-06-01 00:00:00.000','031','1900-01-01 01:00:00.000','1900-01-01 02:00:00.000','2016-08-11 17:58:00.000','d.baltas'),
('C/E',0,1,'2016-06-02 00:00:00.000','031','1900-01-01 01:00:00.000','1900-01-01 02:00:00.000','2016-08-10 17:58:00.000','d.baltas'),
('C/E',1,1,'2016-06-01 00:00:00.000','031','1900-01-01 03:00:00.000','1900-01-01 04:00:00.000','2016-08-10 17:58:00.000','d.baltas'),
('MSTR',0,0,'2016-06-02 00:00:00.000','031','1900-01-01 16:00:00.000','1900-01-01 22:00:00.000','2016-08-10 17:58:00.000','d.baltas'),
('MSTR',0,1,'2016-06-01 00:00:00.000','031','1900-01-01 08:00:00.000','1900-01-01 12:00:00.000','2016-08-10 17:58:00.000','d.baltas'),
('MSTR',1,0,'2016-06-03 00:00:00.000','031','1900-01-01 08:00:00.000','1900-01-01 14:00:00.000','2016-08-11 15:00:00.000','d.baltas');

--create and populate tally table if you don't already a permanent one - arbitrary 1000 rows for demo...you should figure out if that is enough
declare @Tally table (N int PRIMARY KEY);
insert  @Tally
select  top (1000) row_number() over (order by o1.object_id) from sys.columns o1, sys.columns o2 order by 1;
--select    * from @Tally order by N;

with ranked_slots_cte as (  --ranked slots by vessel/rank for each combination of indicator
        select  *, row_number() over (partition by VesselCode, RankCode, SeaPortInd, WatchKeepingInd order by FromDate) time_slot
        from    @WorkingHoursSchedule
        --where EffectiveDate <= getdate() and (LastDate is null or LastDate > getdate())   --it looks like this might be a good place to check these values
        --where VesselCode = '031'  --I assumed getting schedules for all vessels with grouping, but you could filter here instead.
)
select      max_slots.VesselCode, max_slots.RankCode, slots.N TimeSlot
            , substring(convert(VARCHAR(255), r1.FromDate, 120), 11, 6) + ' -' + substring(convert(VARCHAR(255), r1.ToDate, 120), 11, 6)
            , substring(convert(VARCHAR(255), r2.FromDate, 120), 11, 6) + ' -' + substring(convert(VARCHAR(255), r2.ToDate, 120), 11, 6)
            , substring(convert(VARCHAR(255), r3.FromDate, 120), 11, 6) + ' -' + substring(convert(VARCHAR(255), r3.ToDate, 120), 11, 6)
            , substring(convert(VARCHAR(255), r4.FromDate, 120), 11, 6) + ' -' + substring(convert(VARCHAR(255), r4.ToDate, 120), 11, 6)
from        (--total slots per vessel/rank
            select  VesselCode, RankCode, max(time_slot) max_slot
            from    ranked_slots_cte
            group   by VesselCode, RankCode
            --order by VesselCode, RankCode
            ) max_slots
join        @Tally slots    --create the appropriate number of slots per vessel/rank
            on slots.N <= max_slots.max_slot
--join each of the appropriate indicator combinations by ranked time slot
left join   ranked_slots_cte r1     --At Sea/Watch
            on  r1.VesselCode = max_slots.VesselCode 
            and r1.RankCode = max_slots.RankCode
            and r1.time_slot = slots.N
            and r1.SeaPortInd = 1
            and r1.WatchKeepingInd = 1
left join   ranked_slots_cte r2     --At Sea/Not Watch
            on  r2.VesselCode = max_slots.VesselCode 
            and r2.RankCode = max_slots.RankCode
            and r2.time_slot = slots.N
            and r2.SeaPortInd = 1
            and r2.WatchKeepingInd = 0
left join   ranked_slots_cte r3     --In Port/Watch
            on  r3.VesselCode = max_slots.VesselCode 
            and r3.RankCode = max_slots.RankCode
            and r3.time_slot = slots.N
            and r3.SeaPortInd = 0
            and r3.WatchKeepingInd = 1
left join   ranked_slots_cte r4     --In Port/Not Watch
            on  r4.VesselCode = max_slots.VesselCode 
            and r4.RankCode = max_slots.RankCode
            and r4.time_slot = slots.N
            and r4.SeaPortInd = 0
            and r4.WatchKeepingInd = 0
order by    max_slots.VesselCode, max_slots.RankCode, slots.N;

输出如下:

031  C/E   1  03:00 - 04:00  NULL           01:00 - 02:00  00:00 - 00:00
031  C/E   2  NULL           NULL           01:00 - 02:00  05:00 - 07:00
031  MSTR  1  NULL           08:00 - 14:00  08:00 - 12:00  16:00 - 22:00