Sql Упрощение запросов - PullRequest
       7

Sql Упрощение запросов

1 голос
/ 27 января 2020

Схема таблицы

CREATE TABLE [dbo].[PunchClock]
(
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [StaffId] [int] NULL,
    [LocationId] [int] NULL,
    [PunchIn] [datetime] NOT NULL,
    [PunchOut] [datetime] NULL,
    [PunchType] [varchar](1) NULL,
    [IsApproved] [bit] NOT NULL,
    [IsLate] [bit] NOT NULL,
    [ApprovedPunchIn] [datetime] NULL,
    [ApprovedPunchOut] [datetime] NULL,
    [ManagerComments] [nvarchar](100) NULL,
    [PunchInComments] [nvarchar](100) NULL,
    [PunchOutComments] [nvarchar](100) NULL,
    [ApprovedBy] [int] NULL,
    [ApprovedOn] [datetime] NULL,
    [CompanyId] [int] NULL,
    [Deleted] [bit] NOT NULL,
    [UpdatedBy] [int] NULL,
    [UpdatedOn] [datetime] NULL
) ON [PRIMARY]
GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[PunchClock] 
    ADD DEFAULT ((0)) FOR [IsApproved]
GO

ALTER TABLE [dbo].[PunchClock] 
    ADD DEFAULT ((0)) FOR [IsLate]
GO

ALTER TABLE [dbo].[PunchClock] 
    ADD DEFAULT ((0)) FOR [Deleted]
GO

Запрос

SELECT *
FROM 
    (SELECT 
         StaffId,
         [Date],
         PunchIn, PunchOut,
         DayOfWeek,
         TotalHours,
         StaffTotalHour,
         ROW_NUMBER() OVER (PARTITION BY [Date] ORDER BY [Date] DESC) AS [Rank],
         CAST(CONVERT(varchar, DATEADD(SECOND, SUM(TotalBreakHours) OVER (PARTITION BY Date) * 3600, 0), 108) AS time) AS TotalBreakHours,
         CAST(CONVERT(varchar, DATEADD(SECOND, (DATEDIFF(SECOND, CAST(CONVERT(varchar, DATEADD(SECOND, SUM(TotalBreakHours) OVER (PARTITION BY Date) * 3600, 0), 108) AS time), StaffTotalHour) / 3600.0) * 3600, 0), 108) AS time) AS StaffNetHour
      FROM 
          (SELECT 
               *,
               DATENAME(dw, PunchIn) AS [DayOfWeek],
               DATEDIFF(SECOND, PunchIn, PunchOut) / 3600.0 AS TotalHours,
               0 AS TotalBreakHours,
               CAST(CONVERT(varchar, DATEADD(SECOND, (DATEDIFF(SECOND, PunchIn, PunchOut) / 3600.0) * 3600, 0), 108) AS time) AS StaffTotalHour
           FROM 
               (SELECT 
                    StaffID,
                    Date,
                    MIN(PunchIn) AS PunchIn,
                    MAX(PunchOut) AS PunchOut
                FROM 
                    (SELECT 
                         StaffID,
                         CONVERT(date, PunchIn) AS [Date],
                         CASE
                            WHEN PunchOut IS NULL THEN DATEADD(SECOND, 1, CAST(CAST(DATEADD(DAY, 0, CONVERT(date, PunchIn)) AS date) AS datetime2))
                                 ELSE PunchIn
                         END AS PunchIn,
                         CASE 
                            WHEN PunchOut IS NULL THEN PunchIn 
                                 ELSE PunchOut 
                         END AS PunchOut,
                         PunchType
                     FROM
                         (SELECT 
                              StaffID,
                              PunchIn,
                              CASE
                                 WHEN CONVERT(date, PunchIn) < CONVERT(date, PunchOut) THEN DATEADD(SECOND, -1, CAST(CAST(DATEADD(DAY, 1, CONVERT(date, PunchIn)) AS date) AS datetime2))
                                 ELSE PunchOut
                              END AS PunchOut,
                              PunchType
                          FROM 
                              PunchClock) tbl1
                     WHERE 
                         PunchType = 'P') tbl2
                GROUP BY 
                    Date, StaffID) dsag
            UNION ALL
            SELECT *,
                   DATENAME(dw, PunchIn) AS [DayOfWeek],
                   0 AS TotalHours,
                   DATEDIFF(SECOND, PunchIn, PunchOut) / 3600.0 AS TotalBreakHours,
                   '' AS StaffTotalHour
            FROM (SELECT StaffID,
                         Date,
                         MIN(PunchIn) AS PunchIn,
                         MAX(PunchOut) AS PunchOut
                  FROM (SELECT StaffID,
                               CONVERT(date, PunchIn) AS [Date],
                               CASE
                                    WHEN PunchOut IS NULL THEN DATEADD(SECOND, 1, CAST(CAST(DATEADD(DAY, 0, CONVERT(date, PunchIn)) AS date) AS datetime2))
                                    ELSE PunchIn
                               END AS PunchIn,
                               CASE WHEN PunchOut IS NULL THEN PunchIn ELSE PunchOut END AS PunchOut,
                               PunchType
                        FROM (SELECT StaffID,
                                     PunchIn,
                                     CASE
                                          WHEN CONVERT(date, PunchIn) < CONVERT(date, PunchOut) THEN DATEADD(SECOND, -1, CAST(CAST(DATEADD(DAY, 1, CONVERT(date, PunchIn)) AS date) AS datetime2))
                                          ELSE PunchOut
                                     END AS PunchOut,
                                     PunchType
                              FROM PunchClock) tbl1
                        WHERE PunchType = 'B') tbl2
                  GROUP BY Date,
                           StaffID) dsag ) tbl23 ) tblMain
WHERE Rank = 1;

РЕЗУЛЬТАТ

enter image description here Упрощение запроса

Запрос не имеет ошибок или проблем. Он возвращает результаты, которые я хочу, НО проблема в том, что у него много подзапросов, и он не соответствует SQL Стандарту.

Может кто-нибудь упростить это или использовать другой способ получить тот же результат или переписать запрос в более стандартная и простая форма?

1 Ответ

0 голосов
/ 27 января 2020

Я бы начал с преобразования вложенных подзапросов в «вертикальный» конвейер выражений общих таблиц (CTE).

Что-то вроде:

with pc as
(
    SELECT 
        StaffID,
        PunchIn,
        CASE
            WHEN CONVERT(date, PunchIn) < CONVERT(date, PunchOut) THEN DATEADD(SECOND, -1, CAST(CAST(DATEADD(DAY, 1, CONVERT(date, PunchIn)) AS date) AS datetime2))
            ELSE PunchOut END AS PunchOut,
        PunchType
    FROM PunchClock
), pc_p as
(
    SELECT 
        StaffID,
        CONVERT(date, PunchIn) AS [Date],
        CASE
        WHEN PunchOut IS NULL THEN DATEADD(SECOND, 1, CAST(CAST(DATEADD(DAY, 0, CONVERT(date, PunchIn)) AS date) AS datetime2))
                ELSE PunchIn
        END AS PunchIn,
        CASE WHEN PunchOut IS NULL THEN PunchIn  ELSE PunchOut END AS PunchOut,
        PunchType
    FROM pc
    WHERE PunchType = 'P'
), dsag_p as
(
    SELECT 
        StaffID,
        Date,
        MIN(PunchIn) AS PunchIn,
        MAX(PunchOut) AS PunchOut
    FROM pc_p
    GROUP BY Date, StaffID
), pc_b AS
(
    SELECT StaffID,
        CONVERT(date, PunchIn) AS [Date],
        CASE
            WHEN PunchOut IS NULL THEN DATEADD(SECOND, 1, CAST(CAST(DATEADD(DAY, 0, CONVERT(date, PunchIn)) AS date) AS datetime2))
            ELSE PunchIn
        END AS PunchIn,
        CASE WHEN PunchOut IS NULL THEN PunchIn ELSE PunchOut END AS PunchOut,
        PunchType
    FROM  pc
    WHERE PunchType = 'B'
), dsag_b
(
    SELECT StaffID,
        Date,
        MIN(PunchIn) AS PunchIn,
        MAX(PunchOut) AS PunchOut
    FROM pc_b
    GROUP BY Date, StaffID
), tbl123 as
(
    SELECT *,
        DATENAME(dw, PunchIn) AS [DayOfWeek],
        DATEDIFF(SECOND, PunchIn, PunchOut) / 3600.0 AS TotalHours,
        0 AS TotalBreakHours,
        CAST(CONVERT(varchar, DATEADD(SECOND, (DATEDIFF(SECOND, PunchIn, PunchOut) / 3600.0) * 3600, 0), 108) AS time) AS StaffTotalHour
    FROM  dsag_p
    UNION ALL
    SELECT *,
        DATENAME(dw, PunchIn) AS [DayOfWeek],
        0 AS TotalHours,
        DATEDIFF(SECOND, PunchIn, PunchOut) / 3600.0 AS TotalBreakHours,
        '' AS StaffTotalHour
    FROM  dsag_b 
), tblMain as
(
SELECT 
        StaffId,
        [Date],
        PunchIn, PunchOut,
        DayOfWeek,
        TotalHours,
        StaffTotalHour,
        ROW_NUMBER() OVER (PARTITION BY [Date] ORDER BY [Date] DESC) AS [Rank],
        CAST(CONVERT(varchar, DATEADD(SECOND, SUM(TotalBreakHours) OVER (PARTITION BY Date) * 3600, 0), 108) AS time) AS TotalBreakHours,
        CAST(CONVERT(varchar, DATEADD(SECOND, (DATEDIFF(SECOND, CAST(CONVERT(varchar, DATEADD(SECOND, SUM(TotalBreakHours) OVER (PARTITION BY Date) * 3600, 0), 108) AS time), StaffTotalHour) / 3600.0) * 3600, 0), 108) AS time) AS StaffNetHour
    FROM tbl23 
)
SELECT *
FROM  tblMain
WHERE Rank = 1;
...