有一种更好的方式来写这个sql查询(联合)?

问题描述 投票:2回答:2

我想知道是否有写这个sql查询一种更好的方式,在性能和稳定性方面。

因为,我想我重复的代码很多次,也许我可以用CASE条款或另外一个做到这一点。

SELECT x.Fecha,x.IdTrabajador,Cast(x.Cantidad AS DECIMAL(18, 2)) AS Cantidad,x.Motivo,x.IdTrabajadorIncidencia
    FROM   (SELECT I.IdTrabajador,I.Fecha,
                Datediff(second, I.HoraIngreso, (SELECT HD.HoraInicio
                    FROM   [rrhh].TrabajadorHorarioDetalle HD
                            INNER JOIN [rrhh].TrabajadorHorario H
                                    ON HD.IdTrabajadorHorario = H.IdTrabajadorHorario
                    WHERE  H.Estado = 1
                            AND HD.Estado = 1
                            AND HD.Dia = Datename(dw, I.Fecha)
                            AND H.IdTrabajador = I.IdTrabajador)) / 3600.0 AS Cantidad,
                'Sobretiempo en hora de ingreso...' AS Motivo,I.IdTrabajadorIncidencia
            FROM   [rrhh].TrabajadorIncidencia I
            WHERE  I.Estado = 1
                   AND I.IdIncidencia = 1) AS x
    WHERE  x.Cantidad > 0.00
    UNION
    SELECT x.Fecha,x.IdTrabajador,Cast(x.Cantidad AS DECIMAL(18, 2)) AS Cantidad,x.Motivo,x.IdTrabajadorIncidencia
    FROM   (SELECT I.IdTrabajador,I.Fecha,
                Datediff(second, (SELECT HD.HoraInicioRefrigerio
                    FROM   [rrhh].TrabajadorHorarioDetalle HD
                            INNER JOIN [rrhh].TrabajadorHorario H
                                    ON HD.IdTrabajadorHorario = H.IdTrabajadorHorario
                    WHERE  H.Estado = 1
                            AND HD.Estado = 1
                            AND HD.Dia = Datename(dw, I.Fecha)
                            AND H.IdTrabajador = I.IdTrabajador), I.HoraInicioRefrigerio) / 3600.0 AS Cantidad,
                'Sobretiempo en hora de inicio de refrigerio...' AS Motivo,I.IdTrabajadorIncidencia
            FROM   [rrhh].TrabajadorIncidencia I
            WHERE  I.Estado = 1
                   AND I.IdIncidencia = 1) AS x
    WHERE  x.Cantidad > 0.00
    UNION
    SELECT x.Fecha,x.IdTrabajador,Cast(x.Cantidad AS DECIMAL(18, 2)) AS Cantidad,x.Motivo,x.IdTrabajadorIncidencia
    FROM   (SELECT I.IdTrabajador,I.Fecha,
                Datediff(second, I.HoraFinRefrigerio, (SELECT HD.HoraFinRefrigerio
                    FROM   [rrhh].TrabajadorHorarioDetalle HD
                        INNER JOIN [rrhh].TrabajadorHorario H
                                ON HD.IdTrabajadorHorario = H.IdTrabajadorHorario
                    WHERE  H.Estado = 1
                        AND HD.Estado = 1
                        AND HD.Dia = Datename(dw, I.Fecha)
                        AND H.IdTrabajador = I.IdTrabajador)) / 3600.0 AS Cantidad,
                'Sobretiempo en hora de término de refrigerio...' AS Motivo,I.IdTrabajadorIncidencia
            FROM   [rrhh].TrabajadorIncidencia I
            WHERE  I.Estado = 1
                   AND I.IdIncidencia = 1) AS x
    WHERE  x.Cantidad > 0.00
    UNION
    SELECT x.Fecha,x.IdTrabajador,Cast(x.Cantidad AS DECIMAL(18, 2)) AS Cantidad,x.Motivo,x.IdTrabajadorIncidencia
    FROM   (SELECT I.IdTrabajador,I.Fecha,
                Datediff(second, (SELECT HD.HoraFin
                    FROM   [rrhh].TrabajadorHorarioDetalle HD
                            INNER JOIN [rrhh].TrabajadorHorario H
                                    ON HD.IdTrabajadorHorario = H.IdTrabajadorHorario
                    WHERE  H.Estado = 1
                            AND HD.Estado = 1
                            AND HD.Dia = Datename(dw, I.Fecha)
                            AND H.IdTrabajador = I.IdTrabajador), I.HoraSalida) / 3600.0 AS Cantidad,
                'Sobretiempo en hora de salida...' AS Motivo,I.IdTrabajadorIncidencia
            FROM   [rrhh].TrabajadorIncidencia I
            WHERE  I.Estado = 1
                   AND I.IdIncidencia = 1) AS x
    WHERE  x.Cantidad > 0.00
    UNION
    --Search Horas Extras (HE25 y HE35) in table TrabajadorIncidencia
    SELECT x.Fecha,x.IdTrabajador,Cast(x.Cantidad AS DECIMAL(18, 2)) AS Cantidad,x.Motivo,x.IdTrabajadorIncidencia
    FROM   (SELECT I.IdTrabajador,I.Fecha,Datediff(second, I.HoraIngreso, I.HoraSalida) / 3600.0 AS Cantidad,'Sobretiempo en hora extra registrada...' AS Motivo,I.IdTrabajadorIncidencia
            FROM   [rrhh].TrabajadorIncidencia I
            WHERE  I.Estado = 1
                   AND ( I.IdIncidencia = 2
                          OR I.IdIncidencia = 4 )) AS x
    WHERE  x.Cantidad > 0.00
    ORDER  BY x.Fecha;

以前的SQL查询工作正常,但也许它可能会更好。谢谢。

sql sql-server
2个回答
1
投票

您可以使用通用表表达式(CTE的)打破了问题了。在下面的例子中我打破了这个问题分解成它的各个部分。我认为最终的结果是更容易跟踪。

不要以为的CTE会产生任何额外的处理。我常常惊讶的有效SQL Server的查询规划如何使用它们。

几个注意事项:有机会的话,因为在DATEDIFF函数中的SELECT被拆除,并用JOIN替换的结果可能会有所不同。我会假设你已经给定谓词一对一的关系,不然你就已经得到的错误。此外,我可能做的剪切和粘贴错误,所以不感到惊讶,如果这个不运行。

WITH TrabajadorIncidenciaDetalleCte AS (
    SELECT I.*, HD.HoraInicio, HD.HoraFinRefrigerio, HD.HoraFin
    FROM [rrhh].TrabajadorIncidencia I
    INNER JOIN [rrhh].TrabajadorHorarioDetalle HD ON HD.Dia = DATENAME(dw, I.Fecha)
    INNER JOIN [rrhh].TrabajadorHorario H ON HD.IdTrabajadorHorario = H.IdTrabajadorHorario AND H.IdTrabajador = I.IdTrabajador
    WHERE  H.Estado = 1 AND HD.Estado = 1 AND I.Estado = 1 AND I.IdIncidencia = 1
)
, IncidenciaCte AS (
    SELECT IdTrabajador,Fecha, DATEDIFF(second, HoraIngreso, HoraInicio) AS Cantidad,
        'Sobretiempo en hora de ingreso...' AS Motivo, IdTrabajadorIncidencia
    FROM TrabajadorIncidenciaDetalleCte
    UNION ALL
    SELECT IdTrabajador,Fecha, DATEDIFF(second, HoraInicioRefrigerio, HoraFinRefrigerio) AS Cantidad,
        'Sobretiempo en hora de término de refrigerio...' AS Motivo, IdTrabajadorIncidencia
    FROM TrabajadorIncidenciaDetalleCte
    UNION ALL
    SELECT IdTrabajador,Fecha, DATEDIFF(second, HoraFin, HoraSalida) AS Cantidad,
        'Sobretiempo en hora de salida...' AS Motivo, IdTrabajadorIncidencia
    FROM TrabajadorIncidenciaDetalleCte
    UNION ALL
    SELECT IdTrabajador, Fecha, DATEDIFF(second, HoraIngreso, HoraSalida) AS Cantidad,
        'Sobretiempo en hora extra registrada...' AS Motivo, IdTrabajadorIncidencia
    FROM [rrhh].TrabajadorIncidencia
    WHERE Estado = 1 AND ( IdIncidencia = 2 OR IdIncidencia = 4 )   
)
SELECT x.Fecha, x.IdTrabajador, 
    CAST((x.Cantidad / 3600.0) AS DECIMAL(18, 2)) AS Cantidad,
    x.Motivo, x.IdTrabajadorIncidencia
FROM IncidenciaCte x
WHERE x.Cantidad > 0.00
ORDER BY x.Fecha;

0
投票

WHERE x.Cantidad > 0.00意味着你消除子查询没有匹配任何行并返回NULL任何结果。

如果子查询返回多行您当前的查询将失败与错误。如果你很高兴没有错误走在不测,那么你可以使用INNER JOIN而不是4个类似的子查询,然后CROSS APPLY ... VALUES四个联接的列值UNPIVOT成行。然后UNION是到最后的分支。

SELECT I.Fecha,
       I.IdTrabajador,
       CAST(x.Cantidad / 3600.0 AS DECIMAL(18, 2)) AS Cantidad,
       x.Motivo,
       I.IdTrabajadorIncidencia
FROM   [rrhh].TrabajadorIncidencia I
       INNER JOIN [rrhh].TrabajadorHorario H
               ON H.IdTrabajador = I.IdTrabajador
                  AND H.Estado = I.Estado
       INNER JOIN [rrhh].TrabajadorHorarioDetalle HD
               ON HD.IdTrabajadorHorario = H.IdTrabajadorHorario
                  AND HD.Estado = H.Estado
       CROSS APPLY ( VALUES (DATEDIFF(second, I.HoraIngreso, HD.HoraInicio), 'Sobretiempo en hora de ingreso...'),
                            (DATEDIFF(second, HD.HoraFinRefrigerio, I.HoraInicioRefrigerio), 'Sobretiempo en hora de inicio de refrigerio...'),
                            (DATEDIFF(second, I.HoraFinRefrigerio, HD.HoraFinRefrigerio), 'Sobretiempo en hora de término de refrigerio...'),
                            (DATEDIFF(second, HD.HoraFin, I.HoraSalida), 'Sobretiempo en hora de salida...') ) x(Cantidad, Motivo)
WHERE  x.Cantidad > 0
       AND I.Estado = 1
       AND I.IdIncidencia = 1
       AND HD.Dia = DATENAME(dw, I.Fecha)
UNION
--Search Horas Extras (HE25 y HE35) in table TrabajadorIncidencia
SELECT x.Fecha,
       x.IdTrabajador,
       CAST(x.Cantidad AS DECIMAL(18, 2)) AS Cantidad,
       x.Motivo,
       x.IdTrabajadorIncidencia
FROM   (SELECT I.IdTrabajador,
               I.Fecha,
               DATEDIFF(second, I.HoraIngreso, I.HoraSalida) / 3600.0 AS Cantidad,
               'Sobretiempo en hora extra registrada...'              AS Motivo,
               I.IdTrabajadorIncidencia
        FROM   [rrhh].TrabajadorIncidencia I
        WHERE  I.Estado = 1
               AND ( I.IdIncidencia = 2
                      OR I.IdIncidencia = 4 )) AS x
WHERE  x.Cantidad > 0.00
ORDER  BY Fecha; 
© www.soinside.com 2019 - 2024. All rights reserved.