none
Obtener registros del inicio de Mes y final de Mes RRS feed

  • Pregunta

  • Hola, les pido su ayuda para resolver esto, necesito obtener el numero de empleados con los que se cuenta al inicio de cada mes y al final de mes y obtener algo como lo siguiente:


    NoEmpleadosAlInicio Mes
    38 1
    40 2
    56 3
    50 4
    45 5

    NoEmpleadosAlFinal Mes
    38 1
    40 2
    56 3
    50 4
    45 5

    Tengo los datos, Número de empleado, fecha de ingreso fecha de baja

    De antemano, Muchas gracias!


    alozada

    martes, 4 de junio de 2019 19:04

Respuestas

  • Hola alozada:

    El problema no es la salida (que un poco si), donde tienes que mejorar para obtener el resultado correcto es en la definición. No es lo mismo que las columnas sean date, o datetime, o varchar. No has expuesto si vienen de una sola tabla....

    La mejor manera para que el resultado sea siempre el apropiado, o lo más cercano posible, es poner la definición del origen. Si te fijas, yo te he puesto un créate table..... y una sentencia de inserción con múltiples valores.

    Si otra persona en los foros, recoge el testigo de lo que yo te he respondido, ya tiene parte del trabajo hecho para poder aportar otra vía. Si esto que yo he realizado, lo haces tú, siempre tendrás una mejor posibilidad de ayuda.

    Una posible solución:

    DECLARE @fechaAñoCurso DATE= '20190101';
    
    WITH cte
    	AS (SELECT tbl.valor
    	    FROM  
    		    (VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)) AS tbl(valor)),
    	fechas
    	AS (SELECT fecha_ingreso
    		    , DATEDIFF(MONTH, '19000101', @fechaAñoCurso) AS CURSO
    		    , DATEDIFF(month, '19000101', fecha_ingreso) AS INGRESO
    		    , DATEDIFF(MONTH, '19000101', ISNULL(fecha_baja, '21000101')) AS BAJA
    		    , e.NumEmp
    	    FROM   
    		    empleadosNominal e),
    	agrupado
    	AS (SELECT SUM(CASE WHEN f.BAJA >= F.CURSO + cte.valor - 1
    						AND INGRESO < (CURSO + cte.valor) - 1
    					    THEN 1
    				END) AS EmpleadosAlInicio
    		    , SUM(CASE WHEN f.BAJA >= F.CURSO + cte.valor
    						AND INGRESO < (CURSO + cte.valor)
    					    THEN 1
    				END) AS EmpleadosAlFinal
    		    , cte.valor AS Mes
    	    FROM   
    		    fechas f
    			    CROSS JOIN cte
    	    GROUP BY cte.VALOR)
    	SELECT a.EmpleadosAlInicio
    		, a.EmpleadosAlFinal
    		, a.Mes
    		, (a.EmpleadosAlInicio + a.EmpleadosAlFinal) as suma
    		, ((a.EmpleadosAlInicio + a.EmpleadosAlFinal)*(1.0)) as sumaConDecimales
    		, ((((a.EmpleadosAlInicio + a.EmpleadosAlFinal)*(1.0)) / 2) * 100) as resultado
    	FROM   
    		agrupado a;

    Puedes quitar las columnas innecesarias, solo te lo he plasmado, paso por paso, en cada columna para que veas como se puede resolver.

    Ya comentas si la solución es lo deseado

    martes, 4 de junio de 2019 22:41

Todas las respuestas

  • Hola alozada:

    Deberías de exponer mucho mejor tu escenario, porque sino las soluciones, que te podemos dar, serán erróneas, y conllevará mucho más tiempo la resolución satisfactoria de tu pregunta.

    create table empleadosNominal (NumEmp int, fecha_ingreso date, fecha_baja date)
    go
    
    insert into empleadosNominal(NumEmp, fecha_ingreso, fecha_baja)
    values
    (1, '20181212',null),
    (2, '20190112',null),
    (3, '20161030','20190304'),
    (4, '20181231','20190205'),
    (5, '20181231','20190226'),
    (6, '20181231','20190330'),
    (7, '20181231',null),
    (8, '20181231',null),
    (9, '20190102',null),
    (10,'20181231','20181231'),
    (11,'20181231',null),
    (12,'20181231',null),
    (13,'20181231','20190227'),
    (14,'20181231',null),
    (15,'20181231','20190130'),
    (16,'20181231',null),
    (17,'20181231','20190330'),
    (18,'20181231',null),
    (19,'20181231','20190405'),
    (20,'20181231',null);
    go

    Voy a partir de la idea, que lo tienes en el mismo conjunto, y que son columnas tipo date.

    Que la fecha de baja puede ser null, sino se ha producido.

    Que la fecha de alta no puede ser null.

    Qué quieres sólo el año en curso. 

    Para poder solucionarlo, vamos a utilizar dos trucos. El primero, es generar una tabla con los números de los meses para realizar un cross join (producto cartesiano), contra el otro conjunto, donde estarán los datos.

    DECLARE @fechaAñoCurso DATE= '20190101';
    
    WITH cte
    	AS (SELECT tbl.valor
    	    FROM  
    		    (VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)) AS tbl(valor)),
    	fechas
    	AS (SELECT fecha_ingreso
    		    , DATEDIFF(MONTH, '19000101', @fechaAñoCurso) AS CURSO
    		    , DATEDIFF(month, '19000101', fecha_ingreso) AS INGRESO
    		    , DATEDIFF(MONTH, '19000101', ISNULL(fecha_baja, '21000101')) AS BAJA
    		    , e.NumEmp
    	    FROM   
    		    empleadosNominal e)
    	SELECT SUM(CASE WHEN f.BAJA >= F.CURSO + cte.valor - 1
    					 AND INGRESO < (CURSO + cte.valor) - 1
    					THEN 1
    			 END) AS EmpleadosAlInicio		, SUM(CASE WHEN f.BAJA >= F.CURSO + cte.valor
    					 AND INGRESO < (CURSO + cte.valor)
    					THEN 1
    			 END) AS EmpleadosAlFinal
    		, cte.valor as Mes
    	FROM   
    		fechas f
    			CROSS JOIN cte
    	GROUP BY cte.VALOR;

    Declare una variable de tabla con la fecha inicial del año en curso, porque esa fecha, la voy a utilizar como desplazamiento.

    Utilizo la diferencia en meses entre una fecha inicial, '01/01/1900' y en este caso, el primer día del mes del año 2019. 

    De la tabla de empleados, obtengo la misma diferencia también en meses para la fecha de ingreso y para la fecha de baja. (si no hay baja, fuerzo una fecha para comparar numéricamente).

    Luego con la salida de estos conjuntos, realizo el cross join ya mencionado de empleados con meses.

    De ello realizo una suma cuando el empleado no esta de baja en el mes en curso, y ya había ingresado, Y en la otra columna lo mismo, pero si restarle el desplazamiento del mes.

    Espero se parezca en algo a lo que buscas.

    Salida

    Ctes correlativos

    https://javifer2.blogspot.com/2019/01/with-cte-tablas-de-expresion-comun-2.html

    Cross join

    https://javifer2.blogspot.com/search/label/cross%20join

    DateDiff

    https://docs.microsoft.com/es-es/sql/t-sql/functions/datediff-transact-sql?view=sql-server-2017

    • Propuesto como respuesta Carlos_Ruiz_M martes, 4 de junio de 2019 20:19
    martes, 4 de junio de 2019 20:15
  • Muchas gracias, creo que va por ahí, pero tienes razón en lo de explicar mejor mi escenario, lo que necesito hacer es calcular la rotación de empleados, apoyándome con el área de Recursos Humanos, me indican que la rotación se calcula con la siguiente formula:

    Total de Ingresos en el mes + Total de Bajas en el mes X 100
    2
    Plantilla Inicial  + Plantilla Final      
    2

    Que esto sería:

    total de ingresos en el mes más total de bajas en el mes entre 2 por 100

    El resultado lo dividimos con el resultado de Plantilla Inicial Mas Plantilla final entre dos.

    Ojala me puedas ayudar, ahora ya me siento abusador, pero eso es lo que tengo que hacer y creo que mi experiencia en SQL no me alcanza aún, muchas gracias!


    alozada

    martes, 4 de junio de 2019 21:16
  • Hola alozada:

    El problema no es la salida (que un poco si), donde tienes que mejorar para obtener el resultado correcto es en la definición. No es lo mismo que las columnas sean date, o datetime, o varchar. No has expuesto si vienen de una sola tabla....

    La mejor manera para que el resultado sea siempre el apropiado, o lo más cercano posible, es poner la definición del origen. Si te fijas, yo te he puesto un créate table..... y una sentencia de inserción con múltiples valores.

    Si otra persona en los foros, recoge el testigo de lo que yo te he respondido, ya tiene parte del trabajo hecho para poder aportar otra vía. Si esto que yo he realizado, lo haces tú, siempre tendrás una mejor posibilidad de ayuda.

    Una posible solución:

    DECLARE @fechaAñoCurso DATE= '20190101';
    
    WITH cte
    	AS (SELECT tbl.valor
    	    FROM  
    		    (VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)) AS tbl(valor)),
    	fechas
    	AS (SELECT fecha_ingreso
    		    , DATEDIFF(MONTH, '19000101', @fechaAñoCurso) AS CURSO
    		    , DATEDIFF(month, '19000101', fecha_ingreso) AS INGRESO
    		    , DATEDIFF(MONTH, '19000101', ISNULL(fecha_baja, '21000101')) AS BAJA
    		    , e.NumEmp
    	    FROM   
    		    empleadosNominal e),
    	agrupado
    	AS (SELECT SUM(CASE WHEN f.BAJA >= F.CURSO + cte.valor - 1
    						AND INGRESO < (CURSO + cte.valor) - 1
    					    THEN 1
    				END) AS EmpleadosAlInicio
    		    , SUM(CASE WHEN f.BAJA >= F.CURSO + cte.valor
    						AND INGRESO < (CURSO + cte.valor)
    					    THEN 1
    				END) AS EmpleadosAlFinal
    		    , cte.valor AS Mes
    	    FROM   
    		    fechas f
    			    CROSS JOIN cte
    	    GROUP BY cte.VALOR)
    	SELECT a.EmpleadosAlInicio
    		, a.EmpleadosAlFinal
    		, a.Mes
    		, (a.EmpleadosAlInicio + a.EmpleadosAlFinal) as suma
    		, ((a.EmpleadosAlInicio + a.EmpleadosAlFinal)*(1.0)) as sumaConDecimales
    		, ((((a.EmpleadosAlInicio + a.EmpleadosAlFinal)*(1.0)) / 2) * 100) as resultado
    	FROM   
    		agrupado a;

    Puedes quitar las columnas innecesarias, solo te lo he plasmado, paso por paso, en cada columna para que veas como se puede resolver.

    Ya comentas si la solución es lo deseado

    martes, 4 de junio de 2019 22:41