T-SQL / cursores

30/10/2003 - 13:06 por cdiaz | Informe spam
Dado que observo que las T-SQL son mucho más rápidas que los cursoses me
gustaría saber si el siguiente caso tendría una buena solución con T-SQL.
(ahora lo tengo hecho como un procedimiento secuencial)

Datos "reales"
<< Tabla1 >>
Código Fecha Obra
1 01-12-2002 Obra1
2 05-07-2002 Obra2
4 vacío vacío

<< Tabla 2 >>
Código Fecha Obra
1 05-01-2003 Obra4
1 10-01-2003 Obra1
4 05-01-2003 Obra1

El significado de estos datos es:

El código 1 entró en la obra1 el 01-12-2002 y salió el 05-01-2003
El código 1 entró en la obra4 el 05-01-2003 y salió el 10-01-2003
El código 1 entró en la obra1 el 10-01-2003 y todavía está en dicha obra1
El código 2 está en la obra2 desde el 05-07-2002. Está todavía en la obra2.
El código 4 ha aparecido en la obra1 el día 05-01-2003

El objetivo de la T-SQL es obtener los días que ha estado cada uno de los
códigos en cada una de las obras desde una fecha determinada. Cuántos días
hay que facturar a cada una de las obras.

La última fecha de facturación fue el 31-12-2002 y la nueva fecha es el
31-01-2003. Los resultados de facturación que se desean obtener son.

Código Obra Desde
Hasta Días
1 Obra1 01-01-2003 (Fecha de la última
facturación +1) 05-01-2003
5
1 Obra1 10-01-2003
31-01-2003 (Fecha de la facturación actual) 20
1 Obra4 05-01-2003
10-01-2003 4
2 Obra2 01-01-2003 (Fecha de la última
facturación +1) 31-01-2003 (Fecha de la facturación actual)
31
4 Obra1 05-01-2003
31-01-2003 (Fecha de la facturación actual) 26

Gracias de antemano

Preguntas similare

Leer las respuestas

#16 Javier Loria
07/11/2003 - 17:33 | Informe spam
Hola Jose Antonio:
Puede revisar algunos tips (en ingles) sobre desempeno de Triggers:
http://www.sql-server-performance.c...tuning.asp
Saludos,


Javier Loria
Costa Rica
Se aprecia la inclusion de DDL (CREATE, INSERTS, etc.)
que pueda ser copiado y pegado al Query Analizer.
La version de SQL y Service Pack tambien ayuda.
Jose Antonio escribio:
Javier, funciona perfectamente.
Gracias por la información.

Saludos.
Jose Antonio
"Javier Loria" escribió en el mensaje
news:
Hola Jose Antonio:
No se si esto estoy sobre-simplificando el problema. Pero
asumamos: /* Codigo de Creacion de Tablas DDL */
CREATE TABLE CabezasPedidos(
NumeroPedido INT NOT NULL
PRIMARY KEY
)

CREATE TABLE Productos(
NumeroProducto INT NOT NULL
PRIMARY KEY,
CantidadBackOrder INT NOT NULL
)

CREATE TABLE LineasPedidos(
NumeroPedido INT NOT NULL,
NumeroLineaPedido INT NOT NULL,
NumeroProducto INT NOT NULL,
CantidadOrdenada INT NOT NULL,
CONSTRAINT PK_LineasPedidos
PRIMARY KEY (NumeroPedido, NumeroLineaPedido),
CONSTRAINT FK_LineasCabezas
FOREIGN KEY (NumeroPedido)
REFERENCES CabezasPedidos(NumeroPedido)
ON DELETE CASCADE,
CONSTRAINT FK_LineasProducto
FOREIGN KEY (NumeroProducto)
REFERENCES Productos(NumeroProducto)
)
/* Fin de Codigo de Creacion de Tablas */
/* Insertar Datos Demo */
INSERT CabezasPedidos(NumeroPedido)
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4

INSERT Productos(NumeroProducto, CantidadBackOrder)
SELECT 10, 3 UNION ALL
SELECT 11, 7 UNION ALL
SELECT 12, 10 UNION ALL
SELECT 13, 10 UNION ALL
SELECT 14, 10

INSERT LineasPedidos(NumeroPedido, NumeroLineaPedido,
NumeroProducto, CantidadOrdenada)
SELECT 1,1,10,3 UNION ALL
SELECT 1,2,11,4 UNION ALL
SELECT 1,3,12,3 UNION ALL
SELECT 2,1,11,3 UNION ALL
SELECT 2,2,12,4 UNION ALL
SELECT 2,3,13,3 UNION ALL
SELECT 3,1,12,3 UNION ALL
SELECT 3,2,13,4 UNION ALL
SELECT 3,3,14,3 UNION ALL
SELECT 4,1,13,3 UNION ALL
SELECT 4,2,14,4
/* Fin de Insertar Datos Demo */

Este codigo crea las 3 tablas y sus relaciones; y un poco de datos.
El codigo del Trigger podria ser algo como:
/****************************************/
/* Trigger para Matener los BackOrder de Pedidos */
/****************************************/
CREATE TRIGGER Del_LineasPedidos
ON LineasPedidos
FOR DELETE
AS
BEGIN
IF EXISTS (SELECT * FROM Deleted)
BEGIN
UPDATE Productos
SET CantidadBackOrder=CantidadBackOrder-
CantidadOrdenada
FROM Productos JOIN
(SELECT NumeroProducto,
SUM(CantidadOrdenada) AS CantidadOrdenada
FROM Deleted
GROUP BY NumeroProducto) AS D ON

Productos.NumeroProducto=D.NumeroProducto END
END

/**************/
/* Fin de Trigger */
/**************/
El Codigo de Trigger requiere un par de comentarios:
a) El IF EXISTS evita la propagacion de los Triggers. Ejemplo si
alguien ejecutara DELETE CabezasPedidos WHERE NumeroPedidos
b) El UPDATE usa una un SUBQUERY que agrupa las Lineas de Pedidos
Borradas (Deleted) por NumeroProducto porque es posible que existan
varias lineas del mismo producto en un Pedido o si se borran varios
Pedidos simultaneamente. Ejemplo todos los pedidos de un cliente.
c) En este codigo no estan implementada ninguna regla de
negocio, pero si hubiera alguna (CantidadBackOrder>0) entonces debes
reportar el error RAISEERROR y hacer ROLLBACK.

El codigo de pruebas:
/* Codigo de Prueba de Funcionalidad */
SELECT * FROM CabezasPedidos
SELECT * FROM Productos
SELECT * FROM LineasPedidos

DELETE CabezasPedidos
WHERE NumeroPedido=4
/* Fin Codigo de Prueba de Funcionalidad */

Espero te sirva.
Saludos,


Javier Loria
Costa Rica
Se aprecia la inclusion de DDL (CREATE, INSERTS, etc.)
que pueda ser copiado y pegado al Query Analizer.
La version de SQL y Service Pack tambien ayuda.
Jose Antonio escribio:
Te explico mi problematica Javier.
Cuando tienes una tabla cabecera de pedidos por ejemplo y otra tabla
de lineas de pedidos, y entre la dos tienes una relacion para que
las lineas se eliminen en cascada si se elimina la cabecera del
pedido.

Bien al tener definido un trigger en la tabla de lineas para que
descuente de la tabla de productos la cantidad que ya no esta
pedida, me encuentro que si anulas una linea del pedido la tabla
deleted
tiene una sola fila con lo cual se lee el producto de esta fila y se
le descuenta la cantidad de forma correcta.

Pero cuando borras la cabecera del pedido la propia relacion de SQL
se encarga de anular las lineas del pedido y el trigger tambien se
dipara ahora, pero la tabla inserted tiene tantas filas como lineas
tenia el pedido.

Cada una con un producto diferente, entonces yo ahora utilizo un
cursor rapido de lectura solo hacia adelante para recorrer la tabla
inserted. Y no he encontrado otra solución por mas que la he
buscado.

Si tienes una solución para esto sin cursores, te agradecería la
expusieses y yo tambien me uniria al club.

Saludos
Jose Antonio
"Javier Loria" escribió en el mensaje
news:
Hola Jose Antonio:
Mas que nada en un trigger NO deberias usar cursores. La razon
es que el codigo del trigger "atraza" la transaccion, aumenta los
bloqueos y en general mata el rendimiento del servidor.
Efectivamente en los triggers las inserciones/actualizaciones y
borrados son multiples, pero precisamente la fuerza de SQL esta en
este tipo de codigo.
Si nos mandas un ejemplo podemos trabajar con algo de codigo.
Saludos,


Javier Loria
Costa Rica
Se aprecia la inclusion de DDL (CREATE, INSERTS, etc.)
que pueda ser copiado y pegado al Query Analizer.
La version de SQL y Service Pack tambien ayuda.

Jose Antonio escribio:
Da la impresión por vuestros comentarios, que nunca hay que usar
cursores. Estoy de acuerdo que hay que intentar evitarlos, pero
hay situaciones como en los triggers con inserciones,
actualizaciones y borrados multiples donde son imprescindibles.

Saludos.
Jose Antonio
"Miguel Egea" escribió en el mensaje
news:
¡Así me gusta Liliana! Creando adeptos.

=>>>>>> Miguel Egea
http://www.portalsql.com
Microsoft SQL-SERVER MVP.
Brigada Anti-Cursores
Aviso de Seguridad


















http://www.microsoft.com/spain/tech...9-USER.asp
==>>>>>>
"Liliana Sorrentino" escribió en
el mensaje news:
Hola,
¡Uno más para la brigada anti-cursores!
Esto no es de lo mejor en plan de ejecución, pero depende
también de la cantidad de información que manejes. Igual sigo
trabajando a ver si puedo mejorarlo.
Acabo de ver la propuesta de Javier, tendrás que ver los tiempos
en función de la cantidad de datos.
Saludos... Liliana.

DECLARE @desde datetime, @hasta datetime
SELECT @desde = '20030101', @hasta = '20030131'
SELECT t1.codigo, t1.obra,
CONVERT(CHAR(10), case when t1.fecha < @desde THEN @desde ELSE
t1.fecha END, 112),
CONVERT(CHAR(10), ISNULL((select min(fecha) from #tabla2 where
t1.codigo = codigo AND t1.fecha < fecha), @hasta), 112),
DATEDIFF(dd, CASE WHEN t1.fecha < @desde THEN @desde ELSE
t1.fecha END, ISNULL((SELECT MIN(fecha) FROM #tabla2
WHERE t1.codigo = codigo AND t1.fecha < fecha), @hasta))
FROM #tabla1 t1
WHERE t1.fecha is not null
UNION
SELECT t2.codigo, t2.obra,
CONVERT(CHAR(10), t2.fecha, 112),
CONVERT(CHAR(10),
ISNULL( (SELECT MIN(fecha) FROM #tabla1 WHERE t2.codigo >>>>>>> codigo and t2.fecha < fecha),
ISNULL( (SELECT MIN(fecha) FROM #tabla2 t22 WHERE t2.codigo >>>>>>> t22.codigo and t2.fecha < t22.fecha), @hasta)), 112),
DATEDIFF(dd, t2.fecha,
ISNULL( (SELECT MIN(fecha) FROM #tabla1 WHERE t2.codigo >>>>>>> codigo and t2.fecha < fecha),
ISNULL( (SELECT MIN(fecha) FROM #tabla2 t22 WHERE t2.codigo >>>>>>> t22.codigo and t2.fecha < t22.fecha), @hasta)))
FROM #tabla2 t2
ORDER BY 2, 3, 4, 5

"cdiaz" escribió en el mensaje
news:#
Cuando hablo de cursores hago referencia a SQL. Yo tiendo a
utilizar el típico algoritmo de recorrer hasta fin de fichero e
ir procesando registro a registro. Pues bien, leyendo mensajes
de este grupo (últimamente de Liliana Sorrentino) hay muchas
soluciones hechas con T-SQL que yo hubiera hecho con un cursor.
Si comparo los tiempos de respuesta entre el cursor y la T-SQL
, ésta última es mucho más rápida.

Respecto a mi problema, se usan 2 tablas porque éstas proceden
de un sistema migrado de otras bases de datos. La Tabla1
serían los datos maestros de los códigos y la Tabla2 serían
los datos de los movimientos del mes de dichos códigos. La
Tabla1 contiene la
fecha
y la obra de la situación a principio de mes del código. Existe
también en este sistema migrado un histórico de movimientos con
los registros así:

Código Fecha Obra
1 01-12-2002 Obra1
1 05-01-2003 Obra4
1 10-01-2003 Obra1
2 05-07-2002 Obra2
4 05-01-2003 Obra1

Se lanza el proceso de facturación y el resultado se desea que
sea como el comentado en los anteriores mensajes. Si se puede
obtener
el mismo resultado leyendo de este histórico serviría también.
Hay que tener en cuenta que en este histórico existen muchos
registros pues son los datos de los movimientos desde el año
96.

Gracias


"Accotto Maximiliano Damian"
escribió en el mensaje
news:uIIr%
Hola!! 2 temas

1) vos cuando hablas de cursores hablas de SQL o de
programacion como vb?

Yo aconsejo no usar T-sql para programar ya que no es un
lenguaje de programacion como VB,c# o alguno de ellos.
Por mas que T-SQL nos permita hacer triggers y store, estos si
se empiezan a complicar no es aconsejable usar T-SQL (por lo
que lei en nuevas versiones de SQL tendra incorporado .Net).

Bien pasando a tu problema porque no me explicas porque usas 2
tablas y mejor aun el analisis del sistema, asi entiendo si el
aplicar esas 2 tablas de esa forma es correcto o hay una forma
mas optima de hacer la misma tarea.

Un abrazo

Accotto Maximiliano Damian
"cdiaz" escribió en el
mensaje news:
Dado que observo que las T-SQL son mucho más rápidas que los
cursoses me gustaría saber si el siguiente caso tendría una
buena solución con T-SQL. (ahora lo tengo hecho como un
procedimiento secuencial)

Datos "reales"
<< Tabla1 >>
Código Fecha Obra
1 01-12-2002 Obra1
2 05-07-2002 Obra2
4 vacío vacío

<< Tabla 2 >>
Código Fecha Obra
1 05-01-2003 Obra4
1 10-01-2003 Obra1
4 05-01-2003 Obra1

El significado de estos datos es:

El código 1 entró en la obra1 el 01-12-2002 y salió el
05-01-2003 El código 1 entró en la obra4 el 05-01-2003 y
salió el 10-01-2003 El código 1 entró en la obra1 el
10-01-2003 y todavía está en dicha obra1 El código 2 está
en la obra2 desde el 05-07-2002. Está todavía en la obra2.
El código 4 ha aparecido en la obra1 el día 05-01-2003

El objetivo de la T-SQL es obtener los días que ha estado
cada uno de los códigos en cada una de las obras desde una
fecha determinada. Cuántos días hay que facturar a cada una
de las obras.

La última fecha de facturación fue el 31-12-2002 y la nueva
fecha es el 31-01-2003. Los resultados de facturación que se
desean obtener son.

Código Obra Desde
Hasta






Días
1 Obra1 01-01-2003 (Fecha de
la última facturación +1) 05-01-2003
5
1 Obra1 10-01-2003
31-01-2003 (Fecha de la facturación actual) 20
1 Obra4 05-01-2003
10-01-2003
4 2 Obra2 01-01-2003 (Fecha de
la última facturación +1) 31-01-2003 (Fecha
de la facturación actual) 31
4 Obra1 05-01-2003
31-01-2003 (Fecha de la facturación actual) 26

Gracias de antemano
email Siga el debate Respuesta Responder a este mensaje
Ads by Google
Help Hacer una pregunta AnteriorRespuesta Tengo una respuesta
Search Busqueda sugerida