lunes, 25 de noviembre de 2019

Reflexion Final

Reflexión: Austin Hakanson

Al comenzar a realizar este proyecto final, el proceso fue diferente a los anteriores en donde la mayor parte del tiempo se pasaba buscando información en internet sobre cómo funciona T-SQL, los diferentes tipos de sentencias, objetos (Stored procederes, triggers, funciones, vistas, etc.) para poder implementar lo que se solicitaba en la tarea así como las buenas prácticas de programación que ya para esta tarea se vuelven más naturales.  En esta ocasión me podía concentrar más en la lógica del programa y pude sentir la diferencia entre la primera tarea programada y esta en cuanto a conocimiento sobre diseño de bases de datos y sobre la programación en SQL. 

No obstante, en este proyecto aprendí bastantes cosas nuevas. Algunas sentencias nuevas que se implementaron incluyen el uso de CASES en la sentencia SELECT para escoger entre varios valores calculados cuál asignar a un campo determinado, la creación de funciones escalares para hacer cálculos personalizados repetitivos específicos, un uso más riguroso de los JOIN para mantener integridad referencial y relacionar todas las tablas del modelo con el fin de hacer actualizaciones e inserciones de datos masivas (sin iteración) que es algo distinto a lo aplicado en las tares programadas anteriores, aprendí más sobre el manejo de fechas y las operaciones que se pueden realizar con ellas (DATEDIFF, DATEADD, EOMONTH, CONVERT, etc) para satisfacer requerimientos muy específicos para cualquier modelo de negocios. 

En cuanto a organización, en este tarea programada la disciplina fue total, se llevó un registro de todas las horas trabajadas por medio de un bloc de notas que luego se publicaba en este blog. En otras palabras, se aplicó el concepto de bitácora de manera adecuada. También se implementó correctamente el uso de GitHub, aprendí a realizar los salvamentos de versiones desde consola, para mantener un control de versiones de todas las tablas, stored procederes, triggers y funciones de la base de datos, una práctica que considero muy enriquecedora ya que será útil en futuros proyectos y en el entorno laboral para realizar trabajo en equipo. En este proyecto se practicó tener un branch por cada miembro y un master final.

Reflexión: Antony

El SQL ha sido un lenguaje más que interesante, incluso todavía cuando ya iba como por la mitad del periodo del curso no pensé que SQL tuviera tantas aplicaciones y herramientas para llevar a cabo tareas tan diversas.
Este proyecto fue muy curioso, porque fue prácticamente algo así como la suma de los conocimientos de las tres anteriores tareas programadas, ya que con ellas fue que verdaderamente aprendimos mucho de lo que nos podía ofrecer SQL y distintas estrategias para utilizar las herramientas que ofrece el lenguaje, además se aprendió programación que aunque no se aplicó para este proyecto final, ya llevamos el conocimiento para poder hacer una aplicación web que se conecte con nuestra base de datos y podamos utilizar la información que almacenemos en esta.
Además este proyecto tuvo la particularidad de que al ser casi que finales del semestre nos permitió reunirnos más para trabajar en grupo lo cual si bien está bastante bonito prácticamente se trabaja en una sola computadora ya que como no se tiene un servidor donde los dos trabajemos con exactamente la misma base de datos entonces es más fácil trabajar en una sola computadora a tener que estar creando archivos enviándolos y pegándolos en la base de datos, esto al menos para el momento de trabajar reunidos ya a distancia por supuesto no hay de otra

Horas totales trabajadas: 30 h

Trabajo del 25 de noviembre

Hora Inicio: 6:54 a.m

Responsables: Austin Hakanson

Actividades: 

- Se descubre que no es necesario el uso de un case en el select de @Pagos del día porque si el DATEDIFF() entre la hora final de la jornada y la hora de salida de la asistencia es 0, al multiplicar se anule cualquier salario por horas extra. Estos son los estatutos viejos:

-- Caso para horas extra regulares
CASE WHEN DATEDIFF(HOUR,TJ.HoraFin, A.HoraSalida) > 0
THEN SPH.Salario * DATEDIFF(HOUR,A.HoraEntrada, A.HoraSalida) + SPH.Salario * DATEDIFF(HOUR,TJ.HoraFin,A.HoraSalida) * 1.5 
-- Caso para horas ordinarias
ELSE SPH.Salario * DATEDIFF(HOUR,A.HoraEntrada, A.HoraSalida) END

Este es el nuevo:

SPH.Salario * DATEDIFF(HOUR,A.HoraEntrada, A.HoraSalida) + SPH.Salario * DATEDIFF(HOUR,TJ.HoraFin,A.HoraSalida) * 1.5

- Se registran en los movimientos de planilla los pagos por horas ordinarias y horas extraordinarias. Se utiliza CAST() por primera vez para convertir el numero de horas extra a varchar y agregarlo a la descripción de pago de horas extra. https://www.c-sharpcorner.com/blogs/convert-integer-to-string-in-sql-server

- Se modifica la tabla crédito para que siga el modelo de agregación visto en clase. También se añade la tabla de deducción que no se había agregado para crear la especialización por herencia de movimiento planilla en movimientos de crédito y de deducción.

- Se corrigen algunos errores al registrar movimientos de planilla como que el salario base se pague siempre y que por aparte se agreguen la horas extra, antes si tenía horas extra solo agregaba estas.

- Se agrego la tabla credito a Reiniciar tablas y se inicia la especialización de los movimientos de planilla en credito.

- Hay un problema al seleccionar los movimientos de planilla que deben ingresarse en crédito.

Hora pausa: 9:42 a.m
Hora reanuda: 10:38 a.m

Actividades:

- Se corrige el problema para agregar créditos con herencia de movimientos de planilla, código resultante:

- Se realizan pruebas para determinar el numero entre el cual hay que dividir las deducciones para ser aplicadas semanalmente

DECLARE @fecha date = '2017-05-30'
DECLARE @fechaActual date = '2017-06-27'
DECLARE @UltimoViernesDelMes datetime = dbo.ObtenerUltimoDiaDelMes(@fechaActual,'friday')
SELECT CAST(@UltimoViernesDelMes as date)
DECLARE @Semanas int
SET @Semanas = DATEDIFF(WEEK,@fecha, @UltimoViernesDelMes)
SELECT @Semanas

- Pausa: 11:41 a.m
- Hora reanudación: 12:22 a.m

Actividades:

- Se corrigió un error con el cálculo de los meses, ahora sí terminan en el último viernes del mes. 

- Se hizo la función para calcular la diferencia de horas bien (tomando el caso de la media noche)

- Se logran hacer las deducciones cada semana. Las porcentuales y las fijas (para estas se divide entre el numero de semanas en el mes)

- Se toma todo el trabajo de ayer y hoy sobre el procesamiento de pagos y deducciones y se escribe en un stored procedure encapsulado en try-catch y un transation.



Hora Fin: 4:40 p.m

Horas totales: 8:09 h

domingo, 24 de noviembre de 2019

Actividades del día 24 de noviembre

Hora Inicio: 7:30 a.m

Responsables: Austin Hakanson

Actividades:

- Se empiezan a hacer los estatutos sql para hacer los cálculos de salario

Pausa: 8:28 a.m
Reanudación: 8:34 a.m

Actividades:

- Se utiliza por primera vez el estatuto case en un select  para especificar si se tiene que pagar horas extra o no.
Resultado:

SELECT E.Nombre, P.Puesto, TJ.Nombre, A.HoraEntrada, A.HoraSalida, SPH.Salario as SalarioPorHora, 
  CASE WHEN DATEDIFF(HOUR,TJ.HoraFin, A.HoraSalida) > 0 THEN SPH.Salario * DATEDIFF(HOUR,A.HoraEntrada, A.HoraSalida) + SPH.Salario * DATEDIFF(HOUR,TJ.HoraFin,A.HoraSalida) * 1.5 
ELSE SPH.Salario * DATEDIFF(HOUR,A.HoraEntrada, A.HoraSalida) END AS SalarioDeLaSemana
FROM Asistencia A
INNER JOIN Jornada J ON J.Id = A.IdJornada
INNER JOIN Empleado E ON E.Id = J.IdEmpleado
INNER JOIN Puesto P ON P.Id = E.IdPuesto
INNER JOIN TipoJornada TJ ON TJ.Id = J.IdTipoJornada
INNER JOIN SalarioPorHora SPH ON SPH.IdPuesto = P.Id and SPH.IdTipoJornada = TJ.Id
GROUP BY E.Nombre, P.Puesto, TJ.Nombre, A.HoraEntrada, A.HoraSalida, SPH.Salario, TJ.HoraFin

Hora Pausa: 9:10 a.m
Hora Reanudación: 10:50 a.m

Actividades:

- Se calcula el horario total a la semana por empleado, ya se suman tanto las horas ordinarias como las extraordinarias.
- Se decide que se tiene que agregar la tabla mes para identificar las planillas mensuales, también se simplifica el stored porcedure para agregar semanas, se piensa en crear un trigger que inicialice la primer planilla mensual y semanal de un usuario al ser contratado.

Hora Pausa: 12:51 p.m
Hora Reanudación: 3:27 p.m

Actividades: 

- Se busca en internet una solución para obtener el ultimo viernes de cada mes y se encuentran 2 soluciones: Una se encontró en un blog donde hay una una función escalar que utiliza los campos vacíos de la tabla master..spt_values para guardar los dias del año y luego en una condición where determina con DATENAME() si es viernes o cualquier día que se desee. Finalmente se agrupan por mes. La segunda solución era crear una tabla calendario y hacer un proceso similar, teniendo una tabla de números auxiliar. Al final se tomó la primera solución porque es más concisa.

- Se crea el sp para agregar meses

- Se crea un trigger para otorgar una planilla mensual y una planilla semanal cuando se contrata a un empleado. Se iniciativa con salarios en 0.

- Se crea una funcion para obtener el id de la semana para la planilla semanal

Hora Pausa: 7:00 p.m
Hora Reanudación: 11:16 p.m

Actividades: 

- Se continua con el desarrollo del pago de los usuarios.
- Se termina la inserción del pago en planilla semanal

Hora Fin: 11:47 p.m

Horas Totales: 7:33 h


Avance sobre el proyecto

Hora Inicio: 12:19 p.m

Pausa: 1:02 p.m
Reanuda: 1:19 p.m

Responsables: Austin Hakanson

Actividades:

- Se hace el sp para agregar nuevas jornadas para los empleados
- Se corrige el sp para agregar las semanas
- Se creo el tipo de variable tabla TipoJornada

Hora Pausa: 2:20 p.m
Hora Reanuda: 4:12 p.m

Actividades:

- Se insertan los movimientos de planilla para las deducciones que se había olvidado agregar.
- Se cambia el diseño de herencia de tipo de deducciones. Se agrega un foreign key en las tablas hijas que referencias a la tabla padre.

Hora Fin: 4:42 p.m

Horas Totales: 2:13 h



sábado, 23 de noviembre de 2019

Trabajo sobre la simulación

Responsables: Austin Hakanson

Hora Inicio: 10:38 p.m
Pausa: 12:04 p.m
Reanudacion: 12:37 p.m
Actividades:
- Se crean las clases tipo tabla Deducciones y Asistencias
- Se termino  el sp para insertar los empleados y asignarles una jornada
- Se agrega el sp para insertar las deducciones de un empleado
- Se inicia el sp para insertar los nuevos tipos de jornada para cada empleado.
- Se llaman los sp anteriores durante la simulación, pasando un xml como parámetro para hacer las inserciones de forma masiva. Se aplican las buenas practicas (begin try, transacción, errores negativos, nomenclatura ordenada).
- Se hicieron cambios sobre el modelo, esta es la imagen del diagrama más actual:



Hora Fin: 3:27 a.m

Total de horas: 5,4 h (Efectivas estimadas 3h)
326 min


viernes, 22 de noviembre de 2019

Trabajo sobre la simulacion

Hora Inicio: 5:03 p.m

Responsables: Austin Hakanson

Actividades:

- Hubo dificultades para entender como hacer la agregación de jornadas para un empleado, pero revisando modelos vistos anteriormente en el curso y repasando la materia se logro avanzar.

- Se crea un nuevo tipo tabla para pasar por parámetro a un stored procedure una tabla de datos. https://codingsight.com/passing-data-table-as-parameter-to-stored-procedures/



Hora Fin: 7:43 p.m (Horas efectivas: 1:30 h)

Segunda reunión de trabajo (ACTUALIZAR)

Hora de Inicio: 12:00 p.m

Responsables: Austin Hakanson y Antony Artavia

Actividades

- Se agregaron estatutos SQL para insertar los SalarioXHora y las Deducciones en el stored procedure SP_CargarDatos
- Se creo el stored procedure para agregar semanas durante la simulación
- Se inicio el stored procedure para agregar empleados.
- Se realizó todo un análisis de cómo se debía hacer el proceso de simulación lo cual llevó a varios cambios en el modelo de la base de datos, de modo que se realizaran correctamente los stored procedure necesarios. Por ejemplo, inicialmente se tenían foreign keys a de jornada, aguinaldo y asistencia en la tabla de empleado. Sin embargo, luego se pensó que era mejor que el id de empleado se pusiera como un FK en las tablas mencionadas puesto a que para crear una jornada o registrar una asistencia se necesita primero tener creado al empleado. Los cambios efectuados se ven reflejados en la nueva versión del modelo:


- Se revisó el modelo físico hasta el momento.

Hora Final: 2:40 p.m

Horas Totales: 2:40 h

jueves, 21 de noviembre de 2019

Creación del modelo físico

Hora Inicio: 9:23 p.m

Actividades


- Se hace el SP para reiniciar las tablas de carga de datos. En este se eliminan las tablas 
- Realizar la primera conexión de todas las entidades con base en el modelo conceptual hecho en clase. El diagrama resultante es el siguiente:














Enlace al repositorio GitHub: SP_ReiniciarTablas.sql

Hora Fin: 10:40 p.m

Horas totales: 1:16 h

lunes, 18 de noviembre de 2019

Inicio de Proyecto Final

Reunión proyecto final: (Austin y Antony)

Hora Inicio: 1:40 p.m

Actividades:
- Creación preliminar de todas las tablas que van a ser necesarias, no hubo problemas con esta parte porque teníamos la guía del modelo conceptual realizado en clase.
- Inicio del desarrollo de sp de simulación. Se realiza únicamente la carga de los archivos de simulación.
- Apertura de un repositorio en GitHub para control de versiones del proyecto. Se crean 3 ramas: master, Austin y Antony para manejar en las ramas de nuestros nombres los cambios que hacemos individualmente y el master para mantener la versión funcional más actual.
- Análisis de diagrama conceptual de la base de datos para la creación de la base de datos física.
- Se inicia la carga de archivos (Deducción, etc)



Hora Fin: 4:44 p.m

Total de horas trabajadas: 3:04 h

miércoles, 6 de noviembre de 2019

Realización de la primera consulta y segunda consulta.

Para la primera consulta se decidió hacer uso de una vista. En este caso primero se intentó encontrar una forma de usar la función agregada DATEADD y restar los resultados para calcular la cantidad de depósitos realizados y los que que se hubieran realizado si el saldo no hubiera quedado negativo, esto estaba generando problemas, sin embargo investigando se descubrió la función agregada DATEDIFF que simplificó mucho más el proceso. Para aprender a usar las vistas fue de gran ayuda este video: https://www.youtube.com/watch?v=MK_dWEcltWY
Además para este caso de puso como criterio de filtración que la fecha inicio de las cuentas objetivo por analizar fueran iguales o menores al 2018-05-31 ya que el último día del archivo de simulación es el 2018-06-30 y como lo indica la sección DOS de la tercera tarea programada los ahorros en la CO son mensuales por lo que esta sería la última fecha posible para hacer un ahorro mensual. Idealmente se incluiría una consulta que calcula este fecha con base en la última fecha del archivo de simulación.

Este es el código resultante de la primera consulta:

CREATE OR ALTER VIEW [dbo].[Vista_CuentasObjetivoQuePerdieronRetirosPorSaldoNegativo]
AS
SELECT CO.Numero_Cuenta,
  CO.id as ID_de_la_Cuenta_Objetivo,
  CO.Descripcion,
  DATEDIFF(MONTH,Fecha_Proximo_Credito,Fecha_Final) as Cantidad_de_depósitos_realizados,
  DATEDIFF(MONTH,Fecha_Inicio, Fecha_Final) as Cantidad_de_depósitos_si_se_hubieran_realizado_todos_los_depósitos,
  CO.Monto_Ahorro * DATEDIFF(MONTH,Fecha_Proximo_Credito,Fecha_Final) as Monto_debitado_real,
  CO.Monto_Ahorro * DATEDIFF(MONTH,Fecha_Inicio, Fecha_Final) as Monto_debitado_si_se_hubieran_realizado_todos_los_depósitos
FROM CuentaObjetivo CO
WHERE CO.Saldo = 0 and CO.Fecha_Proximo_Credito != Fecha_Final and Fecha_Inicio <= '2018-05-31'
GO

Para la segunda consulta, se decidió utilizar un stored procedure. Para realizar esta consulta se tuvo que hacer modificaciones al archivo XML de simulación (Simulación2.xml), puesto a que no sucedía ningún caso en el que una cuenta tuviera 5 multas por exceso de retiros en cajero humano. Por lo tanto en la prueba realizada solo se muestra sobre 1 cuenta la consulta para mostrar que funciona, ya que hacerlo para todas habría tomado mucho tiempo. También sucedió que con las pruebas se descubrió que puede haber más de un mes y más de un año con la mayor cantidad de retiros, por lo que se pasó de usar una variable entera para guardar este valor a usar tablas variables para guardar todos los años y meses donde la cantidad de retiros fue la mayor. Uno de los principales problemas enfrentados fue el de obtener el primer día del mes siguiente a cualquier fecha, para lograrlo se hizo uso de una función encontrada en internet: DATEADD(m,1,DATEADD(m,datediff(m,0,@Fecha_Buscar),0))

Al final lo que faltó de la consulta fue calcular el promedio de retiros por mes.

El código de la segunda consulta es el siguiente

USE [Sistema_Banco]
GO
/****** Object:  StoredProcedure [dbo].[SP_CuentasMultaExcCH_Min5_UltimosNDias]    Script Date: 11/14/2019 5:05:38 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Austin Hakanson>
-- Create date: <11-06-2019>
-- Description: <SP para ejecutar consulta de cuentas que tienen multas por exceso de retiros en cajero humano
-- y lo han hecho al menos 5 veces en los últimos N días>
-- =============================================
ALTER PROCEDURE [dbo].[SP_CuentasMultaExcCH_Min5_UltimosNDias]
-- Parametros
@Dias int
AS
BEGIN
SET NOCOUNT ON;

-- DESCRIPCION
-- Se consultan los id de las cuentas y el numero de apariciones de dicho id en la tabla movimento
-- para todas las cuentas que hayan tenido movimientos del tipo 'multSaldMin' en fechas posteriores
-- a la fecha de busqueda

-- Se declara una tabla variable para cargar las fechas de operacion
SET Language Spanish
DECLARE @Fechas TABLE
(
sec int primary key identity(1,1),
fecha date
)

DECLARE @UltimaFechaDeOperacion date
DECLARE @PrimeraFechaDeOperacion date
DECLARE @MesesDeOperacion int

DECLARE @DocHandle int, @XmlDocument xml
-- Se cargan los datos del XML a la variable XML
SELECT @XmlDocument = F
FROM OPENROWSET (Bulk '\\Mac\Home\Documents\Ing. Computación\IV Semestre\Bases de datos I\IIITP\Simulacion2.xml',Single_BLOB) AS Fechas(F)
-- Se crea una representacion interna del documento XML
EXEC sp_xml_preparedocument @DocHandle OUTPUT, @XmlDocument
-- Execute a SELECT statement using OPENXML rowset provider.
INSERT INTO @Fechas
SELECT fecha
FROM OPENXML (@DocHandle, '/xml/Simulacion/FechaOperacion',1) -- El uno al final indica que la lectura es <atribute-centric>
      WITH (fecha date)
EXEC sp_xml_removedocument @DocHandle

SELECT @UltimaFechaDeOperacion = max(fecha) FROM @Fechas
SELECT @PrimeraFechaDeOperacion = min(fecha) FROM @Fechas
SELECT @UltimaFechaDeOperacion = DATEADD(DAY,1,@UltimaFechaDeOperacion) -- Para que ajustar el total de meses de operacion si es el ultimo dia del mes
SELECT @MesesDeOperacion = DATEDIFF(MONTH,@PrimeraFechaDeOperacion,@UltimaFechaDeOperacion)

-- Se selecciona la ultima fecha de operacion del xml de simulacion
DECLARE @Fecha_Buscar date = DATEADD(DAY,-@Dias,@UltimaFechaDeOperacion)

-- Se insertan las cuentas con multa de exceso de retiros en cajero humano
DECLARE @Cuentas_Con_Multa_Exceso_CH_En_Fecha_Solicitada TABLE
(
idCuenta int,
Frecuencia int
)
INSERT INTO @Cuentas_Con_Multa_Exceso_CH_En_Fecha_Solicitada
SELECT [idCuenta]
  ,COUNT(idCuenta)
FROM [Sistema_Banco].[dbo].[Movimiento]
WHERE Tipo_Movimiento = 'multExcCH' and Fecha > @Fecha_Buscar
GROUP BY idCuenta

-- CANTIDAD PROMEDIO DE RETIROS

-- Se inserta en una tabla todos los movimientos de retiro

DECLARE @TodosLosMovimientosRetiro TABLE
( sec int identity(1,1),
 idCuenta int,
 Fecha date,
 Tipo_Movimiento nvarchar(50)
)
INSERT INTO @TodosLosMovimientosRetiro
SELECT M.idCuenta,
  M.Fecha,
  M.Tipo_Movimiento
FROM Movimiento M
WHERE M.Tipo_Movimiento = 'retiroCH' or M.Tipo_Movimiento = 'retiroCA'

-- Variables para el procesamiento del promedio
DECLARE @Fecha_LimiteInferior date = @PrimeraFechaDeOperacion
DECLARE @Fecha_LimiteSuperior date = DATEADD(MONTH,1,@PrimeraFechaDeOperacion)
SET @Fecha_LimiteSuperior = DATEADD(DAY,-1,@Fecha_LimiteSuperior)
DECLARE @Promedio float
DECLARE @Total float
DECLARE @CantidadMesesSimulacion int
DECLARE @MesConMasRetiros int = 0
DECLARE @MayorCantidadRetiros int  = 0
DECLARE @AnioConMasRetiros int

-- Ciclo para sacar los retiros mensuales de las cuentas con mas de 5 multas

DECLARE @Retiros_En_El_Mes int
DECLARE @Retiros_En_El_Anio int
DECLARE @MesesConMasRetiros TABLE
(
sec int identity(1,1),
mes int,
cantidad int,
nombre nvarchar(20)
)
DECLARE @AniosConMasRetiros TABLE
(
sec int identity(1,1),
anio int,
cantidad int
)

SELECT @Fecha_Buscar as Fecha_A_Buscar
-- Se definen los limites de fecha para hacer los calculos mensuales
SET @Fecha_LimiteInferior = @Fecha_Buscar
SET @Fecha_LimiteSuperior = DATEADD(m,1,DATEADD(m,datediff(m,0,@Fecha_Buscar),0)) -- Se selecciona el primer dia del siguiente mes
SET @Fecha_LimiteSuperior = DATEADD(DAY,-1,@Fecha_LimiteSuperior) -- Se acomoda para que sea el final del mes
SET @UltimaFechaDeOperacion = DATEADD(DAY,-1, @UltimaFechaDeOperacion)

WHILE @Fecha_LimiteSuperior <= @UltimaFechaDeOperacion
BEGIN

-- Se seleccionan el numero de retiros y el id de las cuentas que tienen movimientos de retiroCH o retiraCA Y que tienen al menos 5 multas por exceso de retiros
SELECT @Fecha_LimiteInferior as Fecha_LimiteInferior, @Fecha_LimiteSuperior as Fecha_LimiteSuperior, M.idCuenta, COUNT(M.idCuenta) Numero_Retiros_En_El_Mes
FROM Movimiento M
WHERE M.idCuenta IN (SELECT idCuenta FROM @Cuentas_Con_Multa_Exceso_CH_En_Fecha_Solicitada WHERE Frecuencia >= 5) -- Se seleccionan solo las cuentas con mas de 5 multas por exceso de retiros
 and M.Fecha BETWEEN @Fecha_LimiteInferior and @Fecha_LimiteSuperior -- Se seleccionan solo los
 and (M.Tipo_Movimiento = 'retiroCH' or M.Tipo_Movimiento = 'retiroCA')
GROUP BY idCuenta

-- Se guarda el numero de retiros en el mes
SELECT @Retiros_En_El_Mes = COUNT(M.idCuenta)
FROM Movimiento M
WHERE M.idCuenta IN (SELECT idCuenta FROM @Cuentas_Con_Multa_Exceso_CH_En_Fecha_Solicitada WHERE Frecuencia >= 5) -- Se seleccionan solo las cuentas con mas de 5 multas por exceso de retiros
 and M.Fecha BETWEEN @Fecha_LimiteInferior and @Fecha_LimiteSuperior -- Se seleccionan solo los
 and (M.Tipo_Movimiento = 'retiroCH' or M.Tipo_Movimiento = 'retiroCA')
GROUP BY idCuenta

SET @Retiros_En_El_Anio = @Retiros_En_El_Mes

IF (@Retiros_En_El_Mes >= @MayorCantidadRetiros)
BEGIN
SET @MayorCantidadRetiros = @Retiros_En_El_Mes
SET @MesConMasRetiros = DATEPART(MONTH,@Fecha_LimiteInferior)
SET @AnioConMasRetiros = DATEPART(YEAR,@Fecha_LimiteInferior)

IF (@MesConMasRetiros NOT IN (SELECT mes FROM @MesesConMasRetiros)) -- Si el mes no esta ya
BEGIN
-- Se eliminan los meses que tengan menor o igual cantidad
IF (@Retiros_En_El_Mes > (SELECT max(cantidad) FROM @MesesConMasRetiros))
BEGIN
DELETE @MesesConMasRetiros
END
-- Se inserta el nuevo mes
INSERT INTO @MesesConMasRetiros
VALUES
(
@MesConMasRetiros,
@Retiros_En_El_Mes,
DATENAME(MONTH,@Fecha_LimiteInferior)
)
END
ELSE
BEGIN
UPDATE @MesesConMasRetiros
SET cantidad = @MayorCantidadRetiros
WHERE mes = @MesConMasRetiros
END

IF (@AnioConMasRetiros NOT IN (SELECT anio FROM @AniosConMasRetiros))
BEGIN
-- Se eliminan los anios que tengan menor o igual cantidad
IF (@Retiros_En_El_Anio > (SELECT max(anio) FROM @AniosConMasRetiros))
BEGIN
DELETE @AniosConMasRetiros
END
-- Se inserta el nuevo anio
INSERT INTO @AniosConMasRetiros
VALUES
(
@AnioConMasRetiros,
@Retiros_En_El_Mes
)
END
ELSE
BEGIN
UPDATE @AniosConMasRetiros
SET cantidad = @MayorCantidadRetiros
WHERE anio = @AnioConMasRetiros
END
END

SET @Fecha_LimiteInferior = DATEADD(m,1,DATEADD(m,datediff(m,0,@Fecha_LimiteInferior),0)) -- Se selecciona el primer dia del siguiente mes
SET @Fecha_LimiteSuperior = DATEADD(MONTH,1,@Fecha_LimiteInferior) -- Se selecciona el primer dia del siguiente mes
SET @Fecha_LimiteSuperior = DATEADD(DAY,-1,@Fecha_LimiteSuperior)
----------------------------------------------------------------------------
END

SELECT  @MesConMasRetiros as MesConMasRetiros, @AnioConMasRetiros as AnioConMasRetiros
SELECT * FROM @MesesConMasRetiros
SELECT * FROM @AniosConMasRetiros
END

Horas de trabajo: Primera Consulta (1:30 h) Segunda Consulta: (2h)

martes, 5 de noviembre de 2019

Resumen y autoevaluacion III Parte

  • Administración del tiempo
Para esta tercera parte hubo una administración del tiempo deficiente, en su mayor parte porque esta tercera parte de la tarea programada se realizó prácticamente a finales del semestre y todo el mundo ya estaba siendo inundado de trabajos, así que el tiempo que se le podía dedicar al proyecto de Bases de datos era muy limitado ya que por la naturaleza de la malla curricular de la carrera de Ingenieria en Computacion, normalmente se llevan al menos dos cursos que realizan proyectos programados grandes. Por ello creo que la administracion del tiempo fue complicada y la limitante de la falta de tiempo por tener que invertirla en otros proyectos o tareas no ayuda para nada a la administracion del tiempo. En todo caso, se logró realizar bastante de la tarea programada.
  • Conocimientos sobre triggers, tipo XML de SQl server, manejo de fechas, etc
Los conocimientos adquiridos en este periodo de la tercera parte de la tarea programada fuerona bastante simples, con respecto a lo aprendido sobre el tipo de dato XML de SQL server es algo simple, lo unico esque hay que enterder que comandos utilizar ya que hay comandos por ejemplo si se quiere guardar mas de un alemento o registro de una tabla, el XML va a estar mal colocado si no se le agrega una raiz, ya que en un XML no puede haber mas de un registro en el "primer nivel" por asi decirlo, asi que si no se usa el comando ROOT("NombreDeRaiz") entonces el XML va a tener un error de sintaxis y si se quisiera utilizar para otras operaciones mas adelante, va a haber errores.

En cuanto a los triggers, ya se habian utilizado para la segunda parte de la tarea programada, pero solo se habia utilizado triggers de insercion, en cambio en esta tercera parte se utilizaron de insercion y actualizacion (de eliminacion no, pues nosotros enrealidad NO eliminamos, sino que "desactivamos" un campo en especifico y eso ya significa que el elemento fue eliminado; por eso el trigger de eliminacion es enrealidad de actualizacion), asi que en esta tarea se aprendio sobre los trigger de actualizacion y que enrealidad es como si se hiciera una eliminacion y luego un insercion, al menos al nivel del elemento actualizado. Ya que para obtener la informacion del elemento antes de la actualizacion, hay que usar la palabra recervada "deleted" y para obtener datos de la nueva informacion del elemento se usa la palabra reservada "inserted".

Se aprendió sobre el manejo y el cálculo de fechas por medio de las funciones agregadas, DATEADD() y DATEDIFF() para realizar actualizaciones y cambios en periodos de tiempo.

También se aprendió sobre el uso de las vistas como una manera de seleccionar y filtrar datos que se quieren consultar, ocultando al usuario final los detalles que pueden no ser relevantes.
  • Comunicación, liderazgo y trabajo en equipo
En esta tarea programada hubo una mejor distribución de las tareas, mientras que Austin se encargaba de corregir errores de la 2da tarea programada y realizar las consultas, Antony se enfocó en los triggers para la realización de la bitácora de eventos. Se inició con un poco más de antelación a realizar el trabajo, sin embargo no se pudo completar por completo todo lo solicitado en la tarea.

Horas totales de trabajo: 13.75 h

sábado, 2 de noviembre de 2019

Corrección de los intereses desde la segunda tarea programada

Se recordó que desde la 2da tarea programada había que corregir un error en el cálculo de los intereses y también que se necesitaba agregar los movimientos de multa por incumplimiento del saldo mínimo y multa por exceso de retiros en cajero humano a la tabla de movimientos.

El código resultante fue el siguiente:

Modificación el cálculo de los intereses.

Viejo:

DECLARE @MontoIntereses int
SELECT @MontoIntereses = TCA.Tasa_Interes * @SaldoMinimo_Segun_Tipo_Cuenta_Cerrar/@Cargos_Servicio_Cuenta_Cerrar

Nuevo:

@MontoIntereses = (TCA.Tasa_Interes_Mensual * 0.01 * @saldoMinimoEC_Cerrar)/12

Se tomaron todas las declaraciones de variables y se colocaron al inicio de la simulación (fuera del while) para evitar una "redeclaración" de la variables


Ejemplo de código viejo

-- Se selecciona el id de la cuenta que debe ser procesada
DECLARE @idCuentaCerrarEC int
SELECT @idCuentaCerrarEC = C.idCuenta
FROM @CuentasCerrarEC C
WHERE C.sec = @lo 

-- Se obtiene el id del ultimo estado de cuenta de la cuenta que esta siendo procesada
DECLARE @idUltimoEC_CuentaCerrar int
SELECT @idUltimoEC_CuentaCerrar = max(EC.id)
FROM EstadoCuenta EC
WHERE EC.idCuenta = @idCuentaCerrarEC and EC.Fecha_Final = @fechaIteracion

-- Se obtiene el saldo minimo mantenido durante la vigencia del ultimo estado de cuenta
DECLARE @saldoMinimoEC_Cerrar money
SELECT @saldoMinimoEC_Cerrar = EC.Saldo_Minimo
FROM EstadoCuenta EC
WHERE EC.id = @idCuentaCerrarEC

-- Se obtiene la cantidad de retiros en cajero humano hechos durante la vigencia del ultimo estado de cuenta
DECLARE @QRCH_EC_Cerrar int
SELECT @QRCH_EC_Cerrar = EC.QRCH
FROM EstadoCuenta EC
WHERE EC.id = @idCuentaCerrarEC

-- Se obtiene el id del tipo de cuenta de la cuenta en iteracion
DECLARE @idTCA_Cuenta_Cerrar int
SELECT @idTCA_Cuenta_Cerrar = CA.idTipoCuentaAhorro
FROM Cuenta_Ahorro CA
WHERE CA.id = @idCuentaCerrarEC

-- Se obtiene el maximo de retiros en cajero humano para el tipo de cuenta de la cuenta en iteracion
DECLARE @MaxRCH_Cuenta_Cerrar int
SELECT @MaxRCH_Cuenta_Cerrar = TCA.Maximo_Retiros_Cajero_Humano
FROM TipoCuentaAhorro TCA
WHERE TCA.id = @idTCA_Cuenta_Cerrar

-- Se obtiene el saldo minimo para el tipo de cuenta de la cuenta en iteracion
DECLARE @SaldoMinimo_Segun_Tipo_Cuenta_Cerrar money
SELECT @SaldoMinimo_Segun_Tipo_Cuenta_Cerrar = TCA.Saldo_Minimo
FROM TipoCuentaAhorro TCA
WHERE TCA.id = @idTCA_Cuenta_Cerrar

-- Se obtiene la multa por exceder el maximo de retiros en cajero humano, segun el tipo de cuenta de la cuenta en iteracion
DECLARE @Multa_MaxRCH_Cuenta_Cerrar money
SELECT @Multa_MaxRCH_Cuenta_Cerrar = TCA.Multa_Exceso_Retiro_Cajero
FROM TipoCuentaAhorro TCA
WHERE TCA.id = @idTCA_Cuenta_Cerrar

-- Se obtiene la multa por incumplir el saldo minimo, segun el tipo de cuenta de la cuenta en iteracion
DECLARE @Multa_Saldo_Minimo_Cuenta_Cerrar money
SELECT @Multa_Saldo_Minimo_Cuenta_Cerrar = TCA.Multa_Saldo_Minimo
FROM TipoCuentaAhorro TCA
WHERE TCA.id = @idTCA_Cuenta_Cerrar

-- Se obtiene los cargos de servicio segun el tipo de cuenta de la cuenta en iteracion
DECLARE @Cargos_Servicio_Cuenta_Cerrar money
SELECT @Cargos_Servicio_Cuenta_Cerrar = TCA.Monto_Mensual_Cargos_Servicio
FROM TipoCuentaAhorro TCA
WHERE TCA.id = @idTCA_Cuenta_Cerrar

-- Se calculan intereses
DECLARE @MontoIntereses int
SELECT @MontoIntereses = TCA.Tasa_Interes * @SaldoMinimo_Segun_Tipo_Cuenta_Cerrar/@Cargos_Servicio_Cuenta_Cerrar
FROM TipoCuentaAhorro TCA
WHERE TCA.id = @idTCA_Cuenta_Cerrar

-- Se obtiene el saldo actual de la cuenta en iteracion
DECLARE @SaldoActual_CuentaCerrar money
SELECT @SaldoActual_CuentaCerrar = CA.Saldo
FROM Cuenta_Ahorro CA
WHERE CA.id = @idCuentaCerrarEC

Ejemplo de código nuevo


-- Se selecciona el id de la cuenta que debe ser procesada
SELECT @idCuentaCerrarEC = C.idCuenta
FROM @CuentasCerrarEC C
WHERE C.sec = @lo 

-- Se obtiene el id del ultimo estado de cuenta de la cuenta que esta siendo procesada
SELECT @idUltimoEC_CuentaCerrar = max(EC.id)
FROM EstadoCuenta EC
WHERE EC.idCuenta = @idCuentaCerrarEC and EC.Fecha_Final = @fechaIteracion

-- Se obtiene el saldo minimo mantenido durante la vigencia del ultimo estado de cuenta
-- Se obtiene la cantidad de retiros en cajero humano hechos durante la vigencia del ultimo estado de cuenta
SELECT @saldoMinimoEC_Cerrar = EC.Saldo_Minimo, 
  @QRCH_EC_Cerrar = EC.QRCH
FROM EstadoCuenta EC
WHERE EC.idCuenta = @idCuentaCerrarEC and EC.Fecha_Final = @fechaIteracion

-- Se obtiene el id del tipo de cuenta de la cuenta en iteracion
-- Se obtiene el saldo actual de la cuenta en iteracion
SELECT @idTCA_Cuenta_Cerrar = CA.idTipoCuentaAhorro,
  @SaldoActual_CuentaCerrar = CA.Saldo
FROM Cuenta_Ahorro CA
WHERE CA.id = @idCuentaCerrarEC

-- Se obtiene el maximo de retiros en cajero humano para el tipo de cuenta de la cuenta en iteracion
-- Se obtiene el saldo minimo para el tipo de cuenta de la cuenta en iteracion
-- Se obtiene la multa por exceder el maximo de retiros en cajero humano, segun el tipo de cuenta de la cuenta en iteracion
-- Se obtiene la multa por incumplir el saldo minimo, segun el tipo de cuenta de la cuenta en iteracion
-- Se obtiene los cargos de servicio segun el tipo de cuenta de la cuenta en iteracion
-- Se calculan los intereses
SELECT @MaxRCH_Cuenta_Cerrar = TCA.Maximo_Retiros_Cajero_Humano, 
  @Multa_MaxRCH_Cuenta_Cerrar = TCA.Multa_Exceso_Retiros_Cajero,
  @SaldoMinimo_Segun_Tipo_Cuenta_Cerrar = TCA.Saldo_Minimo,
  @Multa_Saldo_Minimo_Cuenta_Cerrar = TCA.Multa_Saldo_Minimo,
  @Cargos_Servicio_Cuenta_Cerrar = TCA.Monto_Mensual_Cargos_Servicio,
  @MontoIntereses = (TCA.Tasa_Interes_Mensual * 0.01 * @saldoMinimoEC_Cerrar)/12
FROM TipoCuentaAhorro TCA
WHERE TCA.id = @idTCA_Cuenta_Cerrar

Inserción de los movimientos por multa e intereses

Multa por saldo mínimo


-- Si se incumplio con el saldo minimo se aplica la multa correspondiente
IF (@saldoMinimoEC_Cerrar < @SaldoMinimo_Segun_Tipo_Cuenta_Cerrar)
BEGIN
SET @SaldoActual_CuentaCerrar = @SaldoActual_CuentaCerrar - @Multa_Saldo_Minimo_Cuenta_Cerrar

-- Si el saldo despues de aplicar los intereses y multas queda negativo, se deja en cero
IF (@SaldoActual_CuentaCerrar < 0)
BEGIN 
SET @SaldoActual_CuentaCerrar = 0
END
-- Se inserta el movimiento de multa a la tabla de movimientos
INSERT dbo.Movimiento
SELECT @idCuentaCerrarEC, TM.id, 'multSaldMin',
  @fechaIteracion, @Multa_Saldo_Minimo_Cuenta_Cerrar, 
  @SaldoActual_CuentaCerrar, 'Aplicacion de multa por incumplir el saldo minimo'
FROM TipoMovimiento TM 
WHERE TM.Nombre = 'multSaldMin'
END

Multa por saldo exceso de retiros en cajero humano


-- Si se excedio el maximo de retiros en cajero humano se aplica la multa correspondiente
IF (@QRCH_EC_Cerrar > @MaxRCH_Cuenta_Cerrar)
BEGIN
SET @SaldoActual_CuentaCerrar = @SaldoActual_CuentaCerrar - @Multa_MaxRCH_Cuenta_Cerrar

-- Se inserta el movimiento de multa a la tabla de movimientos
INSERT dbo.Movimiento
SELECT @idCuentaCerrarEC, TM.id, 'multExcCH',
  @fechaIteracion, @Multa_MaxRCH_Cuenta_Cerrar, 
  @SaldoActual_CuentaCerrar, 'Aplicacion de multa por exceder el maximo de retiros permitidos en cajero humano'
FROM TipoMovimiento TM 
WHERE TM.Nombre = 'multExcCH'
END

Horas trabajadas: 1.5 h

jueves, 31 de octubre de 2019

Resolucion de problemas anteriores y algunos triggers


Este día resolví lo de XMLAntes y XMLDespues que podían ser vacíos y lo hice haciendo que los campos de XMLAntes y XMLDespues puedan contener nulos ya que no o no existe o simplemente no encontré ninguna forma de crear un XML que fuera vacío, por lo que los XML vacíos son en realidad nulos.

Además realice algunos triggers algunos trigger y pare allí ya que en esta semana estoy hasta la cebolla de trabajos.

A continuación un poco del código que se programó:

CREATE TRIGGER  [dbo].[COInsertados] ON [dbo].[CuentaObjeto]
   AFTER INSERT
AS
BEGIN
                -- SET NOCOUNT ON added to prevent extra result sets from
                -- interfering with SELECT statements.
                SET NOCOUNT ON;

                               --Variables para buscar id de otros elementos
    declare @TipoEvento nvarchar(100) = N'Insertar CO';
                declare @Numero_de_Cuenta nvarchar(50);                                                  --Numero cuenta para buscar el idUsuario
                              
                               --Elementos para el nuevo elemento
                declare @idUsuario int;    --El id del usuario
                declare @Fecha date = GETDATE();
                declare @idTipoEvento int;   --El id del tipo evento
                declare @IP int = 0;
                declare @XMLAntes xml;
                declare @XMLDespues xml;

                Select @Numero_de_Cuenta = NewCO.Numero_Cuenta,                        --Determina NumeroCuenta y idTipoEvento
                                               @idTipoEvento = TE.id                
                from inserted NewCO, TipoEvento TE
                where TE.nombre = @TipoEvento;

                Select @idUsuario = CA.idCliente                           --Id del usuario
                from CuentaAhorro CA
                where CA.Numero_Cuenta = @Numero_de_Cuenta; 

               
                declare @TablaXml TABLE (  --Tabla variable para sacar XML del evento
                               id int NOT NULL,
                               idCuenta int NOT NULL,
                               Saldo money NOT NULL,
                               Fecha_Inicio date NOT NULL,
                               Fecha_Final date NOT NULL,
                               Monto_Ahorro money NOT NULL,
                               Numero_Cuenta nvarchar(50) NOT NULL
                )

                insert into @TablaXml  --Insercion del Evento en tabla variable
                select newCo.id, newCo.idCuenta, newCo.Saldo, newCo.Fecha_Inicio,
                                               newCo.Fecha_Final, newCo.Monto_Ahorro, newCo.Numero_Cuenta
                from inserted newCo

                set @XMLDespues = (Select * from @TablaXml as TablaXml for xml Auto, ELEMENTS XSINIL);  --XML del Evento

                insert into Evento (idTipoEvento, idUser, _IP, Fecha, XMLAntes, XMLDespues)  --Insercion final al evento
                values(@idTipoEvento, @idUsuario, @IP, @Fecha, @XMLAntes, @XMLDespues);

END

Este día solo invertí el proyecto como 1,5 horas.
Tarde tanto porque al principio estaba haciendo malísimo la tabla que se usa para crear el XML