有更好的方法来编写此sql查询(联合)吗?

时间:2019-01-31 21:27:53

标签: sql sql-server

我想知道在性能和稳定性方面是否有更好的方法来编写此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查询工作正常,但可能会更好。 谢谢。

2 个答案:

答案 0 :(得分: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;

答案 1 :(得分:0)

WHERE x.Cantidad > 0.00意味着您消除了子查询不匹配任何行并返回NULL的所有结果。

如果子查询返回多行,您当前的查询将失败并显示错误。如果您很乐意避免出现这种错误,那么可以使用INNER JOIN而不是4个类似的子查询,然后使用CROSS APPLY ... VALUES将四个联接的列值取消透视成行。然后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;