Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   MS SQL Server (https://www.clubdelphi.com/foros/forumdisplay.php?f=23)
-   -   Sentencia que abarque los meses del año. (https://www.clubdelphi.com/foros/showthread.php?t=93628)

TiammatMX 12-12-2018 01:15:22

Sentencia que abarque los meses del año.
 
Buen día/tarde/noche, según aplique, compañeros del Foro.

Tengo ésta sentencia en MS SQL Server (la versión más reciente, 2018):

Código SQL [-]
select  cac.mcuenta,
    cac.mnombre,
    ISNULL(inicial.saldo, 0) as inicial,
    ISNULL(enero.haber, 0) as enerohaber,
    ISNULL(enero.debe, 0) as enerodebe,
    ISNULL(enero.saldo, 0) as enerosaldo,
    ISNULL(febrero.haber, 0) as febrerohaber,
    ISNULL(febrero.debe, 0) as febrerodebe,
    ISNULL(febrero.saldo, 0) as febrerosaldo,
    ISNULL(marzo.haber, 0) as marzohaber,
    ISNULL(marzo.debe, 0) as marzodebe,
    ISNULL(marzo.saldo, 0) as marzosaldo,
.
.
.
.
.
    ISNULL(diciembre.haber, 0) as diciembrehaber,
    ISNULL(diciembre.debe, 0) as diciembredebe,
    ISNULL(diciembre.saldo, 0) as diciembresaldo
from cacuent as cac
left join (
  select tr.tcuenta, sum(tdebe) as debe, sum(thaber) as haber, sum(tdebe)-sum(thaber) as saldo
  from cdtrans as tr
  where left(tperiod, 4) < 2018
  and tcuenta is not null
  group by tcuenta) as inicial on cac.MCUENTA = inicial.TCUENTA

left join (
  select tr.tcuenta, sum(tdebe) as debe, sum(thaber) as haber, sum(tdebe)-sum(thaber) as saldo
  from cdtrans as tr
  where left(tperiod, 4) = 2018
  and right(tperiod, 2) = 1
  and tcuenta is not null
  group by tcuenta) as enero on cac.MCUENTA = enero.TCUENTA

left join (
  select tr.tcuenta, sum(tdebe) as debe, sum(thaber) as haber, sum(tdebe)-sum(thaber) as saldo
  from cdtrans as tr
  where left(tperiod, 4) = 2018
  and right(tperiod, 2) = 2
  and tcuenta is not null
  group by tcuenta) as febrero on cac.MCUENTA = febrero.TCUENTA

left join (
  select tr.tcuenta, sum(tdebe) as debe, sum(thaber) as haber, sum(tdebe)-sum(thaber) as saldo
  from cdtrans as tr
  where left(tperiod, 4) = 2018
  and right(tperiod, 2) = 3
  and tcuenta is not null
  group by tcuenta) as marzo on cac.MCUENTA = marzo.TCUENTA

.
.
.

left join (
  select tr.tcuenta, sum(tdebe) as debe, sum(thaber) as haber, sum(tdebe)-sum(thaber) as saldo
  from cdtrans as tr
  where left(tperiod, 4) = 2018
  and right(tperiod, 2) = 12
  and tcuenta is not null
  group by tcuenta) as diciembre on cac.MCUENTA = diciembre.TCUENTA

La cual es ineficiente, ya que puede o no tener datos para ciertos meses. El problema no es ése, sino que se darán cuenta que es básicamente el año en curso (where left(tperiod, 4) = 2018) e ir cambiando el mes (and right(tperiod, 2) = 'n'). La duda o pregunta es: ¿podrían orientarme respecto al mejoramiento de la sentencia (no soy muy ducho en SQL Server) o qué faltaría para que el rendimiento se incremente?

De antemano, muchas gracias.

Neftali [Germán.Estévez] 12-12-2018 08:43:59

No me hago a la idea (del todo) de lo que quieres conseguir sólo con la estructura de la tabla y los nombres, pero puedes probar con una función o Stored Procedure que te devolviera una tabla (Table valued function).

Puedes revisar la ayuda sobre funciones, pero a veces lo que aclara más las cosas es un ejemplo (sobre todo si no has trabajado nunca con ellas).
Revisa este ejemplo (las respuestas) y te harás una idea de la estructura que puede tener la función que necesitas. Si buscas encontrarás muchos más del estilo.

La idea es definir la tabla y luego realizar uno o varios recorridos (más sencillos que la consulta inicial con JOINs) para ir rellenando las filas/columnas de la tabla a devolver.

Lo siento creo que no me he explicado muy bien... :o:o
Revisa el ejemplo o busca similares a ver si te da idea y de si te puede servir tirar por ese camino.

mamcx 12-12-2018 15:43:51

Cómo coloque en http://www.clubdelphi.com/foros/showthread.php?t=93348, usa el query planner, que es el que te dice que es lo que hace la BD.

Hay mucho que resalta en tu query. De entrada, el que no estes usando campos fecha para las comparaciones.

RIGTH() no se puede optimizar con indices.

Este tipo de consulta se beneficia bastante con el uso de las funciones WINDOW

https://www.brentozar.com/sql-syntax...es-sql-server/

Hay que buscar en reducir esos join, y poner los datos mas secuenciales. Si pones un ejemplo con DATOS, quizás en http://sqlfiddle.com te puedo echar una mano.


P.D: Y no dices que significa "ineficiente"

bucanero 13-12-2018 18:42:54

Hola a todos!!

Revisa si te sirve la instrucción PIVOT de MSSQL que lo que hace es agrupar registro en columnas en función del valor de un campo (ejemplo mes) y para estos temas es útil, aunque tiene algunas limitaciones, como solo poder utilizar un único campo de pivot, con una única función de resultado

Aqui te dejo este ejemplo aplicado a tu sentencia, donde obtienes en un único registro la cuenta y el saldo para cada uno de los doce meses
Código SQL [-]
  SELECT  tcuenta,
  ISNULL([1], 0) as enero, 
  ISNULL([2], 0) as febrero, 
  ISNULL([3], 0) as marzo, 
  ISNULL([4], 0) as abril, 
  ISNULL([5], 0) as mayo, 
  ISNULL([6], 0) as junio, 
  ISNULL([7], 0) as julio,  
  ISNULL([8], 0) as agosto, 
  ISNULL([9], 0) as septiembre, 
  ISNULL([10], 0) as octubre, 
  ISNULL([11], 0) as noviembre, 
  ISNULL([12], 0) as diciembre
  FROM  (
    select tcuenta, month(tperiod) as mes,  sum(tdebe)-sum(thaber) as saldo
    from cdtrans as tr
    where year(tperiod) = year(getdate())
    and tcuenta is not null
    group by  tcuenta, month(tperiod)  
  ) AS SourceTable  
  PIVOT (  
    sum(saldo)  -- aqui es necesario poner siempre una función para obtener resultados
    FOR mes IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])  
  ) AS PivotTable

Y aquí integrándolo con el resto de tu sentencia

Código SQL [-]
select cac.mcuenta, cac.mnombre, 
    saldo.enero, 
    -- ...,  
    saldo.diciembre, 
    inicial.saldo
from cacuent as cac
left join (
  select tr.tcuenta, sum(tdebe) as debe, sum(thaber) as haber, sum(tdebe)-sum(thaber) as saldo
  from cdtrans as tr
  where year(tperiod) = 2006
  and tcuenta is not null
  group by tcuenta
) as inicial on cac.MCUENTA = inicial.TCUENTA
left join (
  SELECT  tcuenta,
  ISNULL([1], 0) as enero, 
  ISNULL([2], 0) as febrero, 
  ISNULL([3], 0) as marzo, 
  ISNULL([4], 0) as abril, 
  ISNULL([5], 0) as mayo, 
  ISNULL([6], 0) as junio, 
  ISNULL([7], 0) as julio,  
  ISNULL([8], 0) as agosto, 
  ISNULL([9], 0) as septiembre, 
  ISNULL([10], 0) as octubre, 
  ISNULL([11], 0) as noviembre, 
  ISNULL([12], 0) as diciembre
  FROM  (
    select tcuenta, month(tperiod) as mes,  sum(tdebe)-sum(thaber) as saldo
    from cdtrans as tr
    where year(tperiod) = 2006
    and tcuenta is not null
    group by  tcuenta, month(tperiod)  
  ) AS SourceTable  
  PIVOT (  
    sum(saldo)  
    FOR mes IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])  
  ) AS PivotTable 
) saldo on cac.MCUENTA = saldo.tcuenta

Para obtener los otros campos (las suma del debe y del haber) tendrías que insertar otros dos bloques mas de JOIN. De esta forma finalmente se te quedarían tan solo 4 bloques JOIN en la consulta (calculo inicial, calculo debe, calculo haber y saldos) frente a los 13 bloques que tienes actualmente.

Un Saludo


La franja horaria es GMT +2. Ahora son las 04:35:46.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi