← maurobernal.com.ar

Etiqueta: sql

  • script para obtener el tamaño de cada tabla e indice en Postgresql

    Poder determinar como está compuesto el tamaño de una base de datos es una tarea fundamental en el rol de un DBA. Esto te permite identificar posibles errores de diseño o segmentaciones futuras necesarias.

    Acontinuación te dejo un script que suele serme útíl cuando trabajo en motores PostgreSQL. En esta caso si bien lo he usado en su versión 16, también aplica para 17 y 18.

    SELECT
        n.nspname AS schema_name,
        c.relname AS table_name,
        CASE
            WHEN c.relkind = 'r' THEN 'Tabla'
            WHEN c.relkind = 'i' THEN 'Índice'
            WHEN c.relkind = 'm' THEN 'Vista Materializada'
            ELSE c.relkind::text
        END AS object_type,
        -- Tamaño total (datos + índices) en Megabytes (MB)
        ROUND((pg_total_relation_size(c.oid) / (1024.0 * 1024.0))::numeric, 2) AS total_size_mb,
        -- Tamaño de la porción de datos o del índice en Megabytes (MB)
        ROUND((pg_relation_size(c.oid) / (1024.0 * 1024.0))::numeric, 2) AS data_or_index_size_mb,
        -- Tamaño de los índices asociados (solo para tablas) en Megabytes (MB)
        CASE
            WHEN c.relkind = 'r' THEN ROUND((pg_indexes_size(c.oid) / (1024.0 * 1024.0))::numeric, 2)
            ELSE NULL
        END AS indexes_size_mb,
        c.reltuples::bigint AS row_count
    FROM
        pg_class c
    LEFT JOIN
        pg_namespace n ON n.oid = c.relnamespace
    WHERE
        n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
        AND c.relkind IN ('r', 'i', 'm')
    ORDER BY
        pg_total_relation_size(c.oid) DESC;
    columnadescripción
    schema_nameEsquema al que pertenece la tabla o índice.
    table_nameNombre de la tabla, índice o vista materializada.
    object_typeTipo de objeto (‘Tabla’, ‘Índice’, ‘Vista Materializada’).
    total_sizeTamaño total de la relación (datos + índices + TOAST). Para índices es su tamaño.
    table_or_index_sizeTamaño de la relación sin sus índices. Para índices, es su tamaño.
    indexes_sizeTamaño total de los índices asociados (solo aplica y se muestra para tablas).
    row_countConteo de filas de la tabla (es una estimación del catálogo).

    Con el script adjunto obtendrás una salida similar a esta:

  • Cambiar el puerto del MS SQL Server

    Primero que nada recordemos que el motor de Microsoft corre bajo los siguientes puertos:

    TCP:1433

    UDP:1433

    Seguido a este te dejamos este post donde te contamos como funciona la seguridad de MS SQL (https://maurobernal.com.ar/blog/blog/consejos-para-asegurar-tu-ms-sql-server/)

    Entendiendo esta ya podemos empezar a cambiar la configuración de nuestro motor, para cambiar su puerto predeterminado. Lo primero que debes hacer es buscar el «SQL Server Configuration Manager» (o administrador de configuración de SQL Server) y buscar la opción de «SQL Server Network Configuration» (Configuración de Red) \ Protocols for MOTOR ,

    y una vez seleccionado buscas la configuración de TCP/IP y le das doble clic. En la nueva ventana emergente ya puedes cambiar el puerto como te mostramos en la imagen.

    Con esto sería suficiente. Solo toca ahora reiniciar el servicio. Puedes hacerlo también desde la misma aplicación.:

    PD: No te olvides de abrir el puerto en tu firewall. Te dejo una ayuda para hacerlo desde CMD. Por ejemplo si elegiste el 11433:

    netsh advfirewall firewall add rule name="SQL Puerto alternativo" dir=in localport="11433" protocol=tcp action=allow

    Si quieres verificar, puedes usar tu SQL Management Studio ( https://maurobernal.com.ar/blog/blog/mssql/como-especificar-el-puerto-en-el-sql-server-management-studio)

    Deja tus comentarios si te son útiles estos consejos…

  • Funciones Analíticas con SQL Server

    En los siguientes artículos que iré creando resumiremos el uso de las siguientes funciones analíticas incorporadas a partir de SQL Server 2012

     

    Estas son:

    CUME_DIST (Transact-SQL) LEAD (Transact-SQL)
    FIRST_VALUE (Transact-SQL) PERCENTILE_CONT (Transact-SQL)
    LAG (Transact-SQL) PERCENTILE_DISC (Transact-SQL)
    LAST_VALUE (Transact-SQL) PERCENT_RANK (Transact-SQL)
  • Tutorial Rank y Dense_Rank

    Aprende a usar Rank() y Dense_Rank()

    Vamos primero con la documentación de estas dos funciones:

    Rank:

    https://docs.microsoft.com/en-us/sql/t-sql/functions/rank-transact-sql

    Dense_Rank

    https://docs.microsoft.com/en-us/sql/t-sql/functions/dense-rank-transact-sql

     

    Entendiendo con un ejemplo

    Primero crearemos un pequeña base de datos, que incluye un listado de vendedores, con sus ventas

    IF EXISTS(SELECT 1 FROM sys.tables WHERE object_id = OBJECT_ID('myTable'))
    BEGIN;
        DROP TABLE [myTable];
    END;
    GO
    
    CREATE TABLE [myTable] (
        [myTableID] INTEGER NOT NULL IDENTITY(1, 1),
        [Vendedor] VARCHAR(255) NULL,
        [Ventas] VARCHAR(100) NULL,
        PRIMARY KEY ([myTableID])
    );
    GO
    
    INSERT INTO myTable([Vendedor],[Ventas]) VALUES('Bowen','$1.60'),('Becker','$31.71'),('Stanton','$95.81'),('Weaver','$60.26'),('Park','$65.37'),('Cook','$80.55'),('Pruitt','$60.42'),('Roberson','$20.23'),('Gilbert','$81.98'),('Emerson','$44.32');
    INSERT INTO myTable([Vendedor],[Ventas]) VALUES('Harper','$1.60'),('Patrick','$98.08'),('Andrews','$35.71'),('Steele','$92.19'),('Moody','$1.90'),('Strong','$68.97'),('Rowland','$23.67'),('Barron','$79.18'),('Schultz','$80.94'),('Andrews','$1.92');
    INSERT INTO myTable([Vendedor],[Ventas]) VALUES('Cleveland','$58.03'),('Head','$46.60'),('Hickman','$25.67'),('Cooke','$7.19'),('Farrell','$25.46'),('Sutton','$29.80'),('Dominguez','$85.95'),('Meadows','$70.26'),('Horne','$32.67'),('Cantu','$49.44');
    INSERT INTO myTable([Vendedor],[Ventas]) VALUES('Mosley','$95.29'),('Gomez','$8.53'),('Noble','$81.25'),('Shaw','$47.67'),('Harper','$23.01'),('Reese','$2.46'),('Brown','$36.73'),('Hardin','$0.65'),('Marshall','$7.98'),('Combs','$60.59');
    INSERT INTO myTable([Vendedor],[Ventas]) VALUES('Hicks','$2.78'),('Cote','$15.58'),('Knight','$91.91'),('Gilliam','$75.10'),('Leblanc','$11.16'),('Franklin','$13.92'),('Powers','$1.45'),('Clark','$36.26'),('Rose','$22.70'),('Hoover','$78.61');
    INSERT INTO myTable([Vendedor],[Ventas]) VALUES('Hines','$45.97'),('Lowery','$29.76'),('Le','$63.35'),('Allison','$96.55'),('Fulton','$34.28'),('Cleveland','$46.02'),('Brewer','$1.60'),('Howe','$1.60'),('Sims','$26.47'),('Fox','$55.71');
    INSERT INTO myTable([Vendedor],[Ventas]) VALUES('Bentley','$28.30'),('Hahn','$81.10'),('Colon','$62.66'),('Le','$3.42'),('House','$90.24'),('Kemp','$93.13'),('Cooper','$85.59'),('Mosley','$46.40'),('Jacobs','$1.58'),('Dickson','$76.56');
    INSERT INTO myTable([Vendedor],[Ventas]) VALUES('Jacobs','$46.47'),('Moon','$77.90'),('Palmer','$52.45'),('Bray','$99.29'),('Franco','$50.64'),('Erickson','$21.77'),('Joyce','$32.19'),('Kline','$4.15'),('Barrera','$71.70'),('Jacobs','$58.56');
    INSERT INTO myTable([Vendedor],[Ventas]) VALUES('Mercer','$95.18'),('Horn','$43.12'),('Mendoza','$42.59'),('Mays','$59.19'),('Shaw','$33.63'),('Sawyer','$58.80'),('Chen','$43.88'),('Dorsey','$32.79'),('Howard','$50.02'),('Marshall','$81.84');
    INSERT INTO myTable([Vendedor],[Ventas]) VALUES('Casey','$2.67'),('Boyer','$39.07'),('Richards','$31.92'),('Larson','$64.99'),('Drake','$42.46'),('Holmes','$28.37'),('Hicks','$21.46'),('Richardson','$89.33'),('Evans','$8.90'),('Crawford','$11.28');
    

     

    Contenido de esta tabla

    Esta tabla desordenada contiene el listado de vendedores, con sus respectivas Ventas

    select * from myTable

    Base para ejemplo de Dense Rank

    Uso de Rank y Dense_Rank para armar ranking

    La idea de estas dos funciones es armar un ranking.

    Semejanza: Ambas, en el caso de que empaten le ponen el mismo número de ranking

    Diferencia: Al caso siguiente luego del empate Rank no le pone el inmediato superior, si no que mantiene la cuenta de la cantidad de elementos que lleva hasta el momento y le pone el siguiente. A diferencia de Dense_Rank que continúa con el siguiente correlativo al del empate

     

    Con Rank

    select *, RANK() over (Order by Ventas) as Ranking from myTable

    Uso de Rank

    Con Dense_Rank

    select *, DENSE_RANK() over ( Order by Ventas) as Ranking from myTable

    Uso de dense_rankts

  • Cuantos VLF es demasiado?

    Entendiendo que es VLF

    De acuerdo al tamaño que tenga nuestro archivo de LOG (.ldf) es importante controlar los VLF (Virtual Log File).

    Los VLF, son los archivos virtuales en que se divide nuestro log, y un exceso de los mismos puede provocar una reducción del rendimiento.

    Verificando los VLF

    • Verificar cuantos VLF tenemos en nuestra base
    • De manera invidual

     

    DBCC LOGINFO
    
    • De forma masiva
    --variables to hold each 'iteration'  
    declare @query varchar(100)  
    declare @dbname sysname  
    declare @vlfs int  
      
    --table variable used to 'loop' over databases  
    declare @databases table (dbname sysname)  
    insert into @databases  
    --only choose online databases  
    select name from sys.databases where state = 0  
      
    --table variable to hold results  
    declare @vlfcounts table  
        (dbname sysname,  
        vlfcount int)  
      
     
     
    --table variable to capture DBCC loginfo output  
    --changes in the output of DBCC loginfo from SQL2012 mean we have to determine the version 
     
    declare @MajorVersion tinyint  
    set @MajorVersion = LEFT(CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)),CHARINDEX('.',CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(max)))-1) 
    
    if @MajorVersion < 11 -- pre-SQL2012 
    begin 
        declare @dbccloginfo table  
        (  
            fileid smallint,  
            file_size bigint,  
            start_offset bigint,  
            fseqno int,  
            [status] tinyint,  
            parity tinyint,  
            create_lsn numeric(25,0)  
        )  
      
        while exists(select top 1 dbname from @databases)  
        begin  
      
            set @dbname = (select top 1 dbname from @databases)  
            set @query = 'dbcc loginfo (' + '''' + @dbname + ''') '  
      
            insert into @dbccloginfo  
            exec (@query)  
      
            set @vlfs = @@rowcount  
      
            insert @vlfcounts  
            values(@dbname, @vlfs)  
      
            delete from @databases where dbname = @dbname  
      
        end --while 
    end 
    else 
    begin 
        declare @dbccloginfo2012 table  
        (  
            RecoveryUnitId int, 
            fileid smallint,  
            file_size bigint,  
            start_offset bigint,  
            fseqno int,  
            [status] tinyint,  
            parity tinyint,  
            create_lsn numeric(25,0)  
        )  
      
        while exists(select top 1 dbname from @databases)  
        begin  
      
            set @dbname = (select top 1 dbname from @databases)  
            set @query = 'dbcc loginfo (' + '''' + @dbname + ''') '  
      
            insert into @dbccloginfo2012  
            exec (@query)  
      
            set @vlfs = @@rowcount  
      
            insert @vlfcounts  
            values(@dbname, @vlfs)  
      
            delete from @databases where dbname = @dbname  
      
        end --while 
    end 
      
    --output the full list  
    select dbname as 'Base', vlfcount  'Cant'
    from @vlfcounts  
    order by vlfcount desc

    En base a la cantidad deberemos seguir el siguiente criterio

    • Menos de 1 MB, ignorar este caso.
    • Hasta  64 MB: 4 VLFs, cada uno de 1/4 del tamaño
    • De 64 MB a 1 GB: 8 VLFs, cada uno de 1/8 del tamaño
    • Mas de 1 GB: 16 VLFs, cada uno  de 1/16 del tamaño

     

    Si obtenemos una cantidad mayor tenemos que:

    • Verificar el tamaño del LOG.
    • Reducir el LOG(*).
    • Crear de nuevo el LOG con un tamaño inicial igual al existente antes de reiniciarlo.

    (*) será conveniente hacer un backup del mismo

     

    -- Detecting and reducing VLFs in SQL Server 2008
    -- Glenn Berry 
    -- June 2010
    -- http://glennberrysqlperformance.spaces.live.com/
    -- Twitter: GlennAlanBerry
    
    -- Switch to your database
    USE ngmetadata;
    GO
    
    -- Check VLF Count for current database
    DBCC LogInfo;
    
    -- Check individual File Sizes and space available for current database
    SELECT name AS [File Name] , physical_name AS [Physical Name], size/128.0 AS [Total Size in MB],
    size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS int)/128.0 AS [Available Space In MB], [file_id]
    FROM sys.database_files;
    
    -- Step 1: Compressed backup of the transaction log (backup compression requires Enterprise Edition in SQL Server 2008)
    BACKUP LOG [ngmetadata] TO  DISK = N'N:\SQLBackups\ngmetadataLogBackup.bak' WITH NOFORMAT, INIT,  
    NAME = N'ngmetadata- Transaction Log  Backup', SKIP, NOREWIND, NOUNLOAD, COMPRESSION, STATS = 1;
    GO
    
    -- Step 2: Shrink the log file
    DBCC SHRINKFILE (N'ngmetadata_log' , 0, TRUNCATEONLY);
    GO
    
    -- Check VLF Count for current database
    DBCC LogInfo;
    
    -- Step 3: Grow the log file back to the desired size, 
    -- which depends on the amount of write activity 
    -- and how often you do log backups
    USE [master];
    GO
    ALTER DATABASE ngmetadata MODIFY FILE (NAME = N'ngmetadata_log', SIZE = 8GB);
    GO
    
    -- Switch back to your database
    USE ngmetadata;
    GO
    
    -- Check VLF Count for current database after growing log file
    DBCC LogInfo;

     

  • Nivel de Fragmentacion de una base de dato

    Nivel de Fragmentación

    Uno de los principales tópicos que se debe verificar es el crecimiento de los archivos que componen una base de datos, como así también el nivel de fragmentación de los índices que la componen.

    Asegurar el mejor rendimiento de una base de datos en MSSQL Server puede ser una tarea labiosa.

    Verifica que bases de datos crecieron de manera automática

    Cuando alguno de los archivos que componen la base (mdf y ldf) se queden sin espacios, y tiene permitido autocrecer (autogrowth) detienen la consulta en curso,  y autoincrementa su tamaño, y continua con la operación inicial. Esto si ocurre muy seguido puede provocar que se reduzca el rendimiento de la base.
    Una forma de detectarlo es consultando el log del SQL Server:

    SELECT
    DatabaseName as Base,
    [FileName] as Archivo,
    CASE EventClass
    			WHEN 92 THEN 'Datos'
    			WHEN 93 THEN 'Log'
    END  as Tipo,
    CONVERT(VARCHAR(20), EndTime - StartTime, 114) as TiempoDemora,
    StartTime as FechaInicio,
    EndTime as FechaFin
     
    
    FROM (
    SELECT pt = REVERSE(SUBSTRING(REVERSE([path]), CHARINDEX('\', REVERSE([path])), 260)) + N'log.trc'
    FROM sys.traces
    WHERE is_default = 1
    ) p
    CROSS APPLY sys.fn_trace_gettable(pt, DEFAULT)
    WHERE EventClass IN (92, 93)
    ORDER BY StartTime DESC

    AutogrowthDe acuerdo al resultado será la base en la que deberemos trabajar:

    • Si aparece la TEMPDB la opción mas recomendable es: Crear un archivos de Datos (.MDF) y un archivo de LOG( ldf) por cada procesador que tiene el servidor. Y el tamaño inicial total de la base debe ser el actual + 10% . Y luego irlo ajustando a medida que se vaya necesitando.

    Verifica el nivel de fragmentación de los indices

    El segundo punto importante es verificar que nivel de fragmentación tienen cada uno de los indices que componen la base de datos. Este punto puede provocar que nuestra base de datos tenga un tamaño mayor que el real. Y provoque que las consultas se demoren un poco mas de lo que deberian:

    Para corroborarlo podemos usar el siguiente script:

    IF OBJECT_ID('tempdb.dbo.#temp') IS NOT NULL
    DROP TABLE #temp
    GO
    CREATE TABLE #temp (
    db SYSNAME DEFAULT DB_NAME(),
    flname SYSNAME,
    size_after_growth DECIMAL(18,2),
    size DECIMAL(18,2),
    space_used DECIMAL(18,2),
    growth INT,
    is_percent_growth BIT,
    PRIMARY KEY CLUSTERED (db, flname)
    )
    DECLARE @SQL NVARCHAR(MAX)
    SELECT @SQL = STUFF((
    SELECT '
    USE [' + name + ']
    INSERT INTO #temp (flname, size_after_growth, space_used, size, growth, is_percent_growth)
    SELECT
    name
    , CASE WHEN is_percent_growth = 1 THEN size * (1 + (growth / 100.)) ELSE size + growth END * 8. / 1024
    , space_used = FILEPROPERTY(name, ''SpaceUsed'') * 8. / 1024
    , size = size * 8. / 1024
    , CASE WHEN is_percent_growth = 1 THEN growth ELSE growth * 8. / 1024 END
    , is_percent_growth
    FROM sys.database_files'
    FROM sys.databases
    WHERE [state] = 0
    FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
    EXEC sys.sp_executesql @SQL
    SELECT
    db
    , flname
    , size_after_growth
    , size
    , space_used
    , CAST(growth AS VARCHAR(10)) + CASE WHEN is_percent_growth = 1 THEN ' %' ELSE ' MB' END
    FROM #temp
    WHERE (is_percent_growth = 0 AND growth < 50)
    OR (is_percent_growth = 1 AND growth < 5)
    OR (size - space_used < 20)

     

    Para correguirlo podemos ejecutar el siguiente script por cada una de nuestras bases de datos:

    DECLARE @SQL NVARCHAR(MAX)
    SELECT @SQL = (
    SELECT '
    ALTER INDEX [' + i.name + N'] ON [' + SCHEMA_NAME(o.[schema_id]) + '].[' + o.name + '] ' +
    CASE WHEN s.avg_fragmentation_in_percent > 30
    THEN 'REBUILD'
    ELSE 'REORGANIZE'
    END + ';
    '
    FROM (
    SELECT
    s.[object_id]
    , s.index_id
    , avg_fragmentation_in_percent = MAX(s.avg_fragmentation_in_percent)
    FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, NULL) s
    WHERE s.page_count > 128 -- > 1 MB
    AND s.index_id > 0 -- <> HEAP
    AND s.avg_fragmentation_in_percent > 5
    GROUP BY s.[object_id], s.index_id
    ) s
    JOIN sys.indexes i ON s.[object_id] = i.[object_id] AND s.index_id = i.index_id
    JOIN sys.objects o ON o.[object_id] = s.[object_id]
    FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
    PRINT @SQL
    EXEC sys.sp_executesql @SQL

     

     

  • Contar días hábiles entre una fecha

    Como contar días entre fechas

    La siguiente función para SQL Server permite contar la cantidad de días que existen entre un rango de fechas sin contar Sábados y Domingos.

    CREATE FUNCTION [dbo].[DiasHabiles](@FechaInicio DATETIME,@FechaFin DATETIME) 
     RETURNS integer 
     AS 
     BEGIN
    
                DECLARE @DiasNormales INT	--Con esta variable calculamos cuantos dias "normales" hay en el rango de fechas 
                DECLARE @DiasHabiles INT  --Con esta variable acumulamos los dias totales 
                DECLARE @Contador INT		--Esta variable nos sirve de contador para saber cuando lleguemos al ultimo dia del rango 
                DECLARE @Aux_Fecha DATETIME	--Esta variable es la que comparamos para saber si el dia que esta calculando es sábado o domingo*
    
                /*Esta par de variables sirven para comparar las dos fechas, si son iguales, la funcion nos regresa un 0*/ 
                DECLARE @Aux_FechaInicio VARCHAR(10) 
                DECLARE @Aux_FechaFin VARCHAR(10) 
    
                --Inicializamos algunas variables 
    
    			SET @DiasNormales = 0 
                SET @DiasHabiles = 0 
                SET @Contador=0 
    
                --Calculamos cuantos dias normales hay en el rango de fechas  
                SELECT @DiasNormales = DATEDIFF(DAY,@FechaInicio,@FechaFin) + 1 
    
                /*Ordenamos el formato de las fechas para que no importando como se proporcionen se comparen igual*/ 
    
                SELECT @Aux_FechaInicio = (SELECT CAST((CAST(datepart(dd,@FechaInicio)AS 
                                       VARCHAR(2))+'/'+ CAST(datepart(mm,@FechaInicio)AS 
                                       VARCHAR(2))+'/'+CAST(datepart(yy,@FechaInicio)AS VARCHAR(4))) as 
                                       varchar(10))) 
                SELECT @Aux_FechaFin = (SELECT CAST((CAST(datepart(dd,@FechaFin)AS 
                                       VARCHAR(2))+'/'+ CAST(datepart(mm,@FechaFin)AS VARCHAR(2))+'/'+ 
                                       CAST(datepart(yy,@FechaFin)AS VARCHAR(4)))as varchar(10))) 
    
    
                --Se comparan las dos fechas 
    
                IF @Aux_FechaInicio <>@Aux_FechaFin 
    			BEGIN 
    				/*Si la diferencia de fechas es igual a dos, es porque solo ha transcurrido un dia, asi que solo se valida que no vaya a marcar dias de mas*/ 
    				IF @DiasNormales = 2 
    				BEGIN 
    					SELECT @DiasHabiles = 1 
    				END 
    				ELSE 
    				BEGIN 
    					WHILE @Contador < @DiasNormales 
    					BEGIN 
    						/*Se Iguala la fecha a que vamos a calcular para saber si es sabado o domingo en la variable @Aux_Fecha sumandole los dias que marque el contador, el cual no debe ser mayor que el numero total de dias que hay en el rango de fechas*/ 
    						SELECT @Aux_Fecha = @FechaInicio + @Contador 
    						/*Utilizando la funcion datepart con el parametro dw que calcula que dia de la semana corresponde una fecha determinada, determinados que no sea sabado (7) o domingo (1)*/ 
    							IF ((datepart(dw,@Aux_Fecha) <> 1) and (datepart(dw,@Aux_Fecha) <> 7) ) 
    							BEGIN
    								/*Si no es sabado o domingo, entonces se suma uno al total de dias que queremos desplegar*/ 
    								SELECT @DiasHabiles = @DiasHabiles + 1 
    							END 
    						--Se suma un dia mas al contador 
    						SELECT @Contador = @Contador + 1 
    					END 
    				END 
    			END 
                ELSE 
                BEGIN 
                --Si fuese cierto que las fechas eran iguales se despliegue cero 
                SELECT @DiasHabiles = 0 
                END 
    
                --Al finalizar el ciclo, la funcion regresa el numero total de dias 
                RETURN(@DiasHabiles) 
    END
    
    GO
    

     

    Su uso es muy simple:

    Select dbo.DiasHabiles('2015/07/13','2015/07/31') as DiasHabiles,DateDiff(d,'2015/07/13','2015/07/31') as DiasCorridos
    
    

     

     

    Resultado_contar_fechas

    En este ejemplo se puede ver la variación que existe entre la función DateDiff y nuestra función.

  • Contar días sin Sábado ni Domingo

    Como contar cuántos días hay en un período sin contar sábados y domingo

    La siguiente es una  función en TSQL de MS SQL Server, la cúal permite saber la cantidad de días que existe en un período determinado sin contar los fines de semana (sábado y domingo).

    --Primeramente declaramos que vamos a crear una funcion, en este caso se llama Dif Dias y recibe dos parámetros, la fecha inicial del período y la final*/ 
    CREATE FUNCTION [dbo].[DifDias](@StartDate DATETIME,@EndDate DATETIME) 
     RETURNS integer 
     AS 
     Begin 
    
    		DECLARE @DaysBetween INT  --Con esta variable calculamos cuantos dias "normales" hay en el rango de fechas 
    		DECLARE @BusinessDays INT  --Con esta variable acumulamos los dias totales 
    		DECLARE @Cnt INT --esta variable nos sirve de contador para saber cuando lleguemos al ultimo dia del rango 
    		DECLARE @EvalDate DATETIME   --esta variable es la que comparamos para saber si el dia que esta calculando es sábado o domingo*
    
    		/*Esta par de variables sirven para comparar las dos fechas, si son iguales, la funcion nos regresa un 0*/ 
    		DECLARE @ini VARCHAR(10) 
    		DECLARE @fin VARCHAR(10) 
    
    		--Inicializamos algunas variables 
    
    		 SET @DaysBetween = 0 
    		 SET @BusinessDays = 0 
    		 SET @Cnt=0 
    
    		--Calculamos cuantos dias normales hay en el rango de fechas  
    		SELECT @DaysBetween = DATEDIFF(DAY,@StartDate,@EndDate) + 1 
    
    		/*Ordenamos el formato de las fechas para que no importando como se proporcionen se comparen igual*/ 
    
    		SELECT @ini = (SELECT CAST((CAST(datepart(dd,@StartDate)AS 
    						VARCHAR(2))+'/'+ CAST(datepart(mm,@StartDate)AS 
    						VARCHAR(2))+'/'+CAST(datepart(yy,@StartDate)AS VARCHAR(4))) as 
    						varchar(10))) 
    		SELECT @fin = (SELECT CAST((CAST(datepart(dd,@EndDate)AS 
    						VARCHAR(2))+'/'+ CAST(datepart(mm,@EndDate)AS VARCHAR(2))+'/'+ 
    						CAST(datepart(yy,@EndDate)AS VARCHAR(4)))as varchar(10))) 
    
    
    		--Se comparan las dos fechas 
    
    		IF @ini <>@fin 
    		 BEGIN 
    
    		/*Si la diferencia de fechas es igual a dos, es porque solo ha transcurrido un dia, asi que solo se valida que no vaya a marcar dias de mas*/ 
    
    		IF @DaysBetween = 2 
    		 BEGIN 
    		 SELECT @BusinessDays = 1 
    		 END 
    		 ELSE 
    		 BEGIN 
    		 WHILE @Cnt < @DaysBetween 
    		 BEGIN 
    
    		/*Se Iguala la fecha a que vamos a calcular para saber si es sabado o domingo en la variable @EvalDate sumandole los dias que marque el contador, el cual no debe ser mayor que el numero total de dias que hay en el rango de fechas*/ 
    
    		SELECT @EvalDate = @StartDate + @Cnt 
    
    		/*Utilizando la funcion datepart con el parametro dw que calcula que dia de la semana corresponde una fecha determinada, determinados que no sea sabado (7) o domingo (1)*/ 
    
    		IF ((datepart(dw,@EvalDate) <> 1) and 
    		 (datepart(dw,@EvalDate) <> 7) ) 
    		 BEGIN 
    
    		/*Si no es sabado o domingo, entonces se suma uno al total de dias que queremos desplegar*/ 
    
    		SELECT @BusinessDays = @BusinessDays + 1 
    		 END 
    
    		--Se suma un dia mas al contador 
    
    		SELECT @Cnt = @Cnt + 1 
    		 END 
    		 END 
    		 END 
    		 ELSE 
    		 BEGIN 
    
    		--Si fuese cierto que las fechas eran iguales se despliegue cero 
    
    		SELECT @BusinessDays = 0 
    		 END 
    		 
    		 
    		 
    		--Al finalizar el ciclo, la funcion regresa el numero total de dias 
    
    		return (@BusinessDays) 
    		 END
    
    GO
    

    Ejemplo de Uso:

    SELECT  dbo.DifDias('06/01/2015', '06/30/2015')  as DiasAbril

    contar dias-sin-sabado-ni-domingo

  • Truncar Fecha en SQL Server

    Porque trabajas todo el día con un motor MS SQL Server, y te toca cambiar los formatos de visualización de las fechas, aquí te dejo una ayuda memoria:

    De la siguiente manera es posible quitar las horas y los segundos a una fecha.

    select getdate()
    
    -- La manera correcta (desde SQL Server 2008)
    select cast(getdate() As Date)
    
    -- La manera correcta(anterior a SQL Server 2008)
    select dateadd(dd, datediff(dd,0, getDate()), 0)

    truncar-fecha

  • Reparar una base dañada de MS SQL Server

    Últimamente me ha tocado quemarme las pestañas por casos de bases de datos dañadas. Una mala infraestructura, y/o un mal plan de backups termina provocando este tipo de post luego de sufrir por un rato…

    Reparar una base dañada

    El siguiente es un error común cuando se daña una base:

    Msg 824, Level 24, State 2, Line 11
    SQL Server detected a logical consistency-based I/O error: incorrect pageid (expected 1:49876; actual 17253:-991032068). It occurred during a read of page (1:49876) in database ID 15 at offset 0x000000185a8000 in file ‘E:\Bases\SBDAXXXX.MDF’.  Additional messages in the SQL Server error log or system event log may provide more detail. This is a severe error condition that threatens database integrity and must be corrected immediately. Complete a full database consistency check (DBCC CHECKDB). This error can be caused by many factors; for more information, see SQL Server Books Online.

    A continuación los pasos que se debería seguir:

    1. Intentar hacer un backup de la base
    2. Obtener la cantidad de registros que hay en ese momento en cada tabla (script adjunto al final)
    3. Colocar la base en modo de usuario único
      ALTER DATABASE SBDAXXXX SET SINGLE_USER WITH ROLLBACK IMMEDIATE

       

    4. Intentar reparar la base
      DBCC CHECKDB(SBDAXXXX,REPAIR_ALLOW_DATA_LOSS)

       

    5. Verificar la cantidad de registros actual (script adjunto al final)
    DECLARE @Tabla VARCHAR(250)
    DECLARE @SQL VARCHAR(2000)
    CREATE TABLE #TablaTemp(ID INT IDENTITY(1,1),Tabla_Nombre VARCHAR(200),Cant_Registros INT)
    
    DECLARE TINFO_CUR CURSOR FOR 
    SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES 
    WHERE TABLE_TYPE='BASE TABLE'
    
    OPEN TINFO_CUR
    FETCH NEXT FROM TINFO_CUR INTO @Tabla
    WHILE @@FETCH_STATUS =0
    BEGIN
    SET @SQL='INSERT INTO #TablaTemp(Tabla_Nombre,Cant_Registros) SELECT '''+@Tabla+''',COUNT(*) FROM '+@Tabla+''
    EXECUTE (@SQL)
    FETCH NEXT FROM TINFO_CUR INTO @Tabla
    END
    CLOSE TINFO_CUR
    DEALLOCATE TINFO_CUR
    
    
    SELECT * FROM #TablaTemp ORDER BY Tabla_Nombre
    
    DROP TABLE #TablaTemp

     

    Si la base está muy dañada posiblemente no se repara, o se pierde información. En ese caso se puede recurrir a un software de 3ro para intentar reparar el .MDF.

Tags

tsql (27)mssql (26)sql (20)devops (20)dotnet (18)docker (15)performance (14)contenedores (11)dotnet10 (10)linux (9)csharp (8)microservicios (7)angular (7)angular21 (7)sql server (6)issabel (6)docker-compose (6)typescript (6)mysql (5).NET (5)