Imagen para determinar la altura de la cabecera
Other Blogs
There are no items in this list.
Links
There are no items in this list.
Our Blogs > Español > Javier Loría
Visita a Teotihuacán

La gente de Datavisión (partner de Microsoft con experticia en SQL) tuvo la gentileza de llevarme a Teotihuacán el pasado 10 de enero. Teotihuacán es uno de los lugares arqueológicos más importantes de América y lugar de visita obligatoria cuando se está en México. Durante esta visita finalmente pude cumplir mi sueño de subir tanto la pirámide del sol como la de la luna. Antes había estado en Teotihuacán pero siempre subía primero la pirámide del sol, y me quedaba sin fuerzas para subir la pirámide de la luna. Esta vez la hice a la inversa, y con duras penas, pero llegue.

En orden acostumbrado: Héctor Eugenio Jiménez, Javier Loria, Miguel Angel Granados y Jesús Gil Velasco.

Subiendo la pirámide de la Luna

Pirámide del Sol, vista desde la pirámide de la Luna

Arriba, en la pirámide del Sol.

Pirámide de la Luna vista desde la pirámide del Sol

 

 

OPIs (Indicadores de Desempeño Operativos): Parte 2

En la primera parte expuse (http://blogs.solidq.com/ES/jloria/Lists/Posts/Post.aspx?ID=25), que con frecuencia las organizaciones no tienen el nivel de madurez de inteligencia de negocios y de planeamiento estratégico necesario para construir paneles de mando o tableros control (Scorecards y/o Dashboards); y estas herramientas deben ser el fruto del planeamiento estratégico y fluyen de arriba (dirección/gerencia) hacia abajo (mandos medios y empleados) en la organización. Quiero resaltar, que no es que no crea en estos componentes, ni mucho menos; creo que son una herramienta invaluable, pero con mayor frecuencia de lo me gustaría reconocer, estos paneles y tableros de control no son herramientas efectivas de administración y no son más que piezas decorativas de software que sirven para satisfacer el ego corporativo y justificar excesos de gasto en infraestructura de BI.

A pesar de esto, es mi opinión los departamentos de tecnología de empresas que no están maduras para implementar una infraestructura de monitoreo (scorecards/dashboards) pueden beneficiarse de las tecnologías de indicadores inteligencia de negocios, si cambian la perspectiva de KPIs por OPIs.

Definiciones:

Empecemos entonces por definir y separar los términos:

KPI: por sus siglas en ingles (Key Performance Indicator): Medida que ayuda a una organización a definir y medir el progreso hacia objetivos estratégicos corporativos.

OPI: por sus siglas en ingles (Operational Performance Indicator): Medida que ayuda a una organización a examinar y monitorear sus resultados operativos.

Los KPIs son fruto del planeamiento estratégico. La dirección o gerencia, evalúa estrategias y desarrolla un plan para garantizar el éxito de dicha estrategia; luego busca cuales son las mejores medidas que indiquen que se mueve la organización en la dirección correcta; más tarde fija objetivos para estas medidas y finalmente evalúa su cumplimiento.

OPIs y Metadata

Los OPIs por el contrario no son fruto del planeamiento estratégico, son fruto del conocimiento operativo (know how) de la empresa. El proceso para definir OPIs, es más simple que el KPIs, se medidas que son relevantes para la empresa y se capturan y se controlan contra un valor esperado que no necesariamente es un objetivo. A pesar de es fácil definir OPIs, requieren mas trabajo operativo por lo que sus características de "medatadata" que le son fundamentales. Debe para cada OPI definirse:

  1. Definición de Negocios: la definición de negocios sirve para que usuarios no expertos en la materia entiendan la naturaleza del OPI. Por ejemplo si se define un OPI sobre la Razón o Prueba del Acido (Razón Financiera) debería ser algo como: Muestra la capacidad de la empresa para hacer frente a sus obligaciones de corto plazo y se calcula como la división de los activos corrientes menos el inventario entre los pasivos corrientes.
  2. Perspectiva: Indica dentro del marco de referencia de establecido por Kaplan & Norton cuál es la perspectiva más importante que se ve afectada por este OPI. Las perspectivas definidas por Kaplan y Norton son: Financiera, Cliente, Interna y Crecimiento/Aprendizaje. Las perspectivas hacen que sea luego más fácil pasar de OPIs a KPIs.
  3. Proceso de Negocios: El agrupar los OPIs por procesos de negocios y no de forma departamental, permiten analizar de forma más integral la empresa.
  4. Propietario de Negocios: Es la persona responsable de dicha medida, es una referencia a la persona a la que debería llamar un directivo/ejecutivo para discutir los resultados de esta OPI. Los propietarios de negocios suelen ser mandos medios de la organización.
  5. Definición Técnica: Esta definición puede ser para los especialistas del negocio y lleva más detalle que la de negocios o es la definición que permite al departamento de tecnología dar trazabilidad a la medida.
  6. Propietario Técnico: Es la persona responsable técnico de la medida, o sea una referencia en el caso de que se crea que la medida no este disponible o se crea que tiene errores de cálculo. Los propietarios técnicos son DBA's o super-usuarios de aplicación.
  7. Intención: Como debe interpretarse la medida cuando se compara con el valor esperado: más es mejor, menos es mejor, medio es mejor o referencia. Por ejemplo: Ventas Totales seria más es mejor, Costo Total es menos es mejor, Apalancamiento Financiero puede ser medio es mejor, y Tipo de Cambio es referencia (no es mejor ni peor, es algo que ocurre y no podemos cambiar).
  8. Tipo y Unidad de Medida: El tipo separa a Flujos (Movimientos), de saldos, razones y tasas. La separación por tipos es fundamental por la relación que con el tiempo tienen cada uno de ellos. Los Flujos pueden agregarse a través del tiempo y suelen moverse con mucha rapidez, los saldos por otra parte no se pueden agregar a través del tiempo y se mueven con mayor lentitud porque no cambian la historia. Las razones y tasas dependen de sus elementos base. Por otra parte la unidad de medida clarifica los valores y evita errores, pueden ser Kilos, Libras, Miles de Dólares, Euros por Semana, etc.
  9. Método de Cálculo del Valor Esperado: Indica cómo se calcula el valor Esperado del KPI:
    1. Valor Histórico: Este valor puede ser un promedio ponderado de resultados histórico. Por ejemplo el promedio de las últimas 5 semanas, o el promedio del año anterior.
    2. Valor Proyectado: Valor que es calculado con un mecanismo de Series de Tiempo, basado únicamente en el mismo OPI.
    3. Valor Estimado: Valor que es calculado basado en modelos de minería más complejos, que analizan las relaciones estadísticas entre diferentes variables.
    4. Promedio de Industria: Valor que es promedio de la industria o sector y sirve como punto de referencia.
    5. Valor Presupuestado/Meta: Es el único valor que es fijado con intervención humana directa, y refleja un objetivo de negocios y no un comportamiento esperado. El fijar metas o presupuestos implica planificación y mueve lentamente a los OPIs hacia los KPIs porque comienzan a reflejar objetivos, se mantienen como OPIs hasta que se defina la medida como estratégica.
  10. Regla de Clasificación: Indica la regla que se usa para clasificar el resultado, cuando compara el valor real con el valor esperado. Esta regla es por naturaleza subjetiva, a menos que sea general o estadística. La general seria indicar los valores como "Amarillo" cuando sean superiores al 95% de la meta para todos los valores (asumiendo que más es mejor). O cuando se utiliza alguna medida de dispersión estadística (ejemplo: varianza o variación estándar) pero de forma general. Finalmente puede ser fijado a mano en cuyo caso es un criterio personal de un especialista de mercado
  11. Peso/Factor: Numero que sirve para asignar mayor valor a las desviaciones de algunos elementos. Se recomienda que el factor sea un número de 1 a 5, fijado por un experto de negocio y constante para evitar su manipulación y afectar los resultados.

Técnicamente hablando los OPIs tienen también dos elementos de Metadata adicionales:

  1. Dimensión/Jerarquía de Responsabilidad: Indica cual es la dimensión natural para navegar esta medida, cuando se quiere establecer responsabilidad sobre los resultados de esta medida en colaboradores de la empresa. Permite que las herramientas de análisis dividan los resultados generales puntualizando aéreas de resultados extraordinarios, pero bajo un marco administrativo jerarquías administrativas.
  2. Dimensión/Nivel de Captura de Tiempo: Indica el nivel más bajo sobre el cual tiene sentido capturar los resultados, desde el punto de vista de negocios. Aunque sea posible técnicamente posible capturar a niveles inferiores, no se reflejara.

    Los OPIs son una comunicación de expertos del negocio hacia los ejecutivos, por eso es crítico que los OPIs completen con información no numérica los valores numéricos.

Paso a Paso de Temas Dinámicos en WPF/Silverlight

El siguiente es un paso a paso de como implementar en una aplicación WPF o de Silverlight con temas que el usuario puede seleccionar de acuerdo a su gusto. Como simplificación solo implantaremos un tema controle el fondo (background) de la ventana principal y el color de los botones. Este paso a paso usa Web Expression 2.

  1. Usando Web Expression 2, inicie un proyecto de WPF.
  2. En la ventana del proyecto agregue las dos imágenes jpg que se usaran como fondo de la ventana.
  3. Renombre las imágenes, como Tema1Back.jpg y Tema2Back.jpg
  4. Agregue al proyecto un archivo de recursos, haciendo clic derecho sobre el proyecto y agregue un ítem tipo recursos. Nombre el archivo Tema1.xaml

  5. Arrastre y deje caer la imagen Tema1Back.jpg a la ventana principal. Esto agregará un control imagen que se usará para crear un "Brush".
  6. Con la imagen recién creada haga en el menú principal seleccione Tools-Make Brush Resource-Make Image Brush.
  7. Nombre al recurso FondoPrincipal y agréguelo al diccionario de recursos Tema1.xaml, y presione OK.

  8. Borre la imagen que sirvió para crear el recurso.
  9. Seleccione la ventana principal (no el grid), seleccione propiedades, y en la sección de "Background" seleccione Brush Resources, y dentro de la lista de Local Brush Resources seleccione el FondoPrincipal. Esto cambiara el fondo de la pantalla principal y lo agregara al archivo Tema1.
  10. A la ventana principal agregue dos botones. Estos botones servirán para cambiar de tema.
  11. Renombre los botones Tema1 y Tema2 y ponga como contenido de cada uno su nombre. Es fundamental que el nombre esté relacionado con el Tema, ya que basado en el nombre del botón (y no en su contenido) se escribirá el código dinámico.
  12. Con uno de los botones seleccionados, cambie el color del fondo y presione la opción de +Brush

  13. Nombre el recurso como Botones Tema y agréguelo al diccionario de recursos Tema1.

de

  1. Seleccione el otro botón y asígnele como fondo, el recurso BotonesTema recién creado, para que ambos botones tengan el mismo color.

  2. Seleccione uno de los botones y en las propiedades, seleccione la opción de eventos, para agregar un evento clic llamado: CambioTema. Cuando presione "Enter", se abrirá Visual Studio, para que pueda editar el código.
  3. Agregue el siguiente código:

Button b = sender as Button;

ResourceDictionary r = new ResourceDictionary();

r.Source = new Uri(b.Name.ToString() + ".xaml", UriKind.Relative);

Application.Current.Resources.MergedDictionaries.Clear();

Application.Current.Resources.MergedDictionaries.Add(r);

  1. Vuelva a Web Expression 2, sin cerrar Visual Studio, y seleccione el otro botón, y asigne al evento clic a CambioTema. Volverá a activarse Visual Studio.
  2. Grabe el código y cierre Visual Studio.
  3. En la ventana de proyecto, seleccione con clic derecho el archivo de recursos Tema1.xaml y escoja Copy (Copiar).
  4. Seleccione el proyecto con clic derecho y escoja Paste (Pegar).
  5. Seleccione el Diccionario de recursos "Copy of Tema1" y renómbrelo como Tema2.
  6. Haga doble clic sobre el App.xaml, para editar la configuración de diccionarios de recursos.
  7. Elimine la referencia a "Copy of Tema1", para que quede de la siguiente forma:

<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Tema1.xaml"/>
</ResourceDictionary.MergedDictionaries>

  1. Grabe y cierre el archivo App.xaml.
  2. Con el archivo de Window1.xmal abierto, seleccione la opción de Recursos (Resources).
  3. Navegue al Diccionario de recursos Tema1 y cambie el color de BotonesFondo.
  4. Haga clic derecho sobre el FondoPrincipal seleccione View XMAL.
  5. Reemplace la referencia a la imagen Tema1Back.jpg por Tema2Back.jpg
  6. Grabe todos los archivos y pruebe el código.

Concatenando Textos en SQL

En un posteo reciente http://blogs.solidq.com/ES/jloria/Lists/Posts/Post.aspx?ID=20 use un "patrón" de comando de SQL que tiene la siguiente forma:

SET @Comando=''
SELECT TOP 10 @Comando=@Comando+CHAR(13)+CHAR(10)+Indice
FROM dbo.FragmentacionIndices
ORDER BY TotalPages DESC;

Este comando permite concatenar un texto usando una variable, pero mi amigo Eladio Rincón, que el equipo de desarrollo de SQL, sostiene que ese comando no está garantizado que brinda resultados consistentes.

Ahora para otro proyecto tengo que concatenar una de nombres que están relacionados con una tabla. Despues de estudiar algunas alternativas me gusta la siguiente solución:

; WITH ProveedoresOrdenados (ProductID, VendorId, NumProveedor) AS

(

SELECT ProductVendor.ProductID

    , ProductVendor.VendorID

    , ROW_NUMBER() OVER ( PARTITION BY ProductID ORDER BY VendorID) AS NumProveedor

FROM Purchasing.ProductVendor

)

, ProdVendedorRecursivo (ProductId, VendorId, Description, NumProveedor)

AS (

    SELECT ProveedoresOrdenados.ProductID

        , ProveedoresOrdenados.VendorId

        , CAST(ProveedoresOrdenados.VendorID AS VARCHAR(8000))

        , ProveedoresOrdenados.NumProveedor

    FROM ProveedoresOrdenados

    WHERE NumProveedor=1

UNION ALL

SELECT ProveedoresOrdenados.ProductId, ProveedoresOrdenados.VendorId

, ProdVendedorRecursivo.Description + ', ' + CAST(ProveedoresOrdenados.VendorId AS VARCHAR(8000))

, ProveedoresOrdenados.NumProveedor

     FROM ProveedoresOrdenados

     JOIN ProdVendedorRecursivo

     ON ProveedoresOrdenados.ProductID=ProdVendedorRecursivo.ProductId

        AND ProveedoresOrdenados.NumProveedor=ProdVendedorRecursivo.NumProveedor+1

)

SELECT ProdVendedorRecursivo.ProductId, ProdVendedorRecursivo.Description

FROM ProdVendedorRecursivo

JOIN (SELECT ProductId, MAX(ProdVendedorRecursivo.NumProveedor) AS NumProveedor

FROM ProdVendedorRecursivo GROUP BY ProductId) AS Last

ON ProdVendedorRecursivo.ProductId=Last.ProductId

AND ProdVendedorRecursivo.NumProveedor=Last.NumProveedor

 

El ejemplo se basa en la BD de AdventureWorks y la tabla ProductVendors. ProductVendors es captura una relación de muchos a muchos entre Products y Vendors el comando pretende obtener para cada producto una columna con la lista de Proveedores (Vendors).

El comando usa 2 CTEs y algunas funciones disponibles desde SQL 2005 para obtener el resultado deseado. La primera consulta usa ROW_NUMBER para obtener un número consecutivo de proveedor para cada producto, este número facilita los JOINS de la segunda consulta. La segunda consulta aprovecha las consultas recursivas para concatenar la descripción, de manera tal que cada línea tiene la descripción de todos los anteriores. Finalmente la consulta filtra y despliega solo la última fila de cada producto.

No estoy seguro de su desempeño en tablas de tamaño considerable.

Llenar Fechas en México, Versión 2

Esta versión corrige algunos errores de la anterior, esa versión puede encontrarse en: http://blogs.solidq.com/ES/jloria/Lists/Posts/Post.aspx?ID=26

SET LANGUAGE Spanish;

GO

ALTER FUNCTION dbo.Pascua (@Yr as int)

RETURNS DATETIME

AS

BEGIN

--    Codigo encontrado en: http://www.tek-tips.com/faqs.cfm?fid=5075

Declare @Cent int, @I int, @J int, @K int, @Metonic int, @EMo int, @EDay int

Set @Cent=@Yr/100

Set @Metonic=@Yr % 19

Set @K=(@Cent-17)/25

Set @I=(@Cent-@Cent/4-(@Cent-@K)/3+19*@Metonic+15) % 30

Set @I=@I-(@I/28)*(1-(@I/28)*(29/(@I+1))*((21-@Metonic)/11))

Set @J=(@Yr+@Yr/4+@I+2-@Cent+@Cent/4) % 7

Set @EMo=3+(@I-@J+40)/44

Set @EDay=@I-@J+28-31*(@EMo/4)

RETURN CAST(CAST(@Yr*10000+@Emo*100+@Eday AS VARCHAR(8)) AS DATETIME);

/*This algorithm is from the work done by JM Oudin in 1940 and is accurate from year 1754 to 3400.*/

END;

GO

 

-- Llena Dimension con Datos

SET LANGUAGE Spanish;

GO

; WITH Fechas(Fecha, DimFechaId) AS

(

SELECT CAST('19010101' AS DATETIME)

    , YEAR(CAST('19010101' AS DATETIME))*10000

    +MONTH(CAST('19010101' AS DATETIME))*100

     +DAY(CAST('19010101' AS DATETIME))

UNION ALL

    SELECT DATEADD(DAY, 1, Fecha), YEAR(DATEADD(DAY, 1, Fecha))*10000+MONTH(DATEADD(DAY, 1, Fecha))*100+DAY(DATEADD(DAY, 1, Fecha))

    FROM Fechas

    WHERE DATEADD(DAY, 1, Fecha)<'20210101'

)

, Festividades (Festividad, Festivo, TipoFeriado, Dia, Lunes)

AS( SELECT 'Dia de todos los Santos' ,'Festivo NO Obligatorio', 'NoAplica',    1101, 0

UNION ALL SELECT 'Dia de Muertos' ,'Festivo NO Obligatorio', 'NoAplica    ',    1102, 0

UNION ALL SELECT 'Aniversario de la Batalla Puebla' ,'Festivo NO Obligatorio', 'NoAplica',        505, 0

UNION ALL SELECT 'Aniversario de la Revolución Mexicana' ,'Festivo Obligatorio', 'Pasar',    1120, 3

    -- 3er Lunes Noviembre

UNION ALL SELECT 'Natalicio de Benito Juarez''s' ,'Festivo Obligatorio', 'Pasar',        321, 3

    -- 3er Lunes Marzo

UNION ALL SELECT 'Navidad', 'Festivo Obligatorio' ,'Fijo', 1225, 0

UNION ALL SELECT 'Dia de la Raza' ,'Festivo NO Obligatorio', 'NoAplica',    1012, 0

UNION ALL SELECT 'Dia de la Bandera' ,'Festivo NO Obligatorio', 'NoAplica', 224, 0

UNION ALL SELECT 'Dia de Independencia' ,'Festivo Obligatorio', 'Fijo', 916, 0

UNION ALL SELECT 'Dia del Trabajo', 'Festivo Obligatorio' ,'Fijo', 501, 0

UNION ALL SELECT 'Año Nuevo' ,'Festivo Obligatorio', 'Fijo',     101, 0

UNION ALL SELECT 'Día de la Virgen de Guadalupe' ,'Festivo Obligatorio', 'Fijo', 1212, 0

UNION ALL SELECT 'Dia de la Madre' ,'Festivo NO Obligatorio', 'NoAplica', 510, 0

UNION ALL SELECT 'Aniversario de la Constitucion' ,'Festivo Obligatorio', 'Pasar', 205, 1

    -- Primer Lunes de Febrero

)

-- Agregar INSERT a la tabla.

SELECT DimFechaId

    , Fechas.Fecha AS Fecha

    , (DimFechaId/10000)*10000+101 AS AnioId

    , CAST(Year(Fechas.Fecha) AS CHAR(4)) AS AnioNombre

    , YEAR(Fechas.Fecha)*10000

        + (1+(DATEPART(Quarter, Fechas.Fecha)-1)*3)*100

        +1 AS TrimestreId

    , 'Q'+CAST(DATEPART(Quarter, Fechas.Fecha) AS VARCHAR(1))

            + '-' +CAST(Year(Fechas.Fecha) AS CHAR(4)) AS TrimestreNombre

    , (DimFechaId/100)*100+1 AS MesId

    , CAST(DATENAME(Month, Fechas.Fecha) AS VARCHAR(25))+ '-' + CAST(Year(Fechas.Fecha) AS CHAR(4)) AS MesNombre

    , case when YEAR(DATEADD(DAY, 1, DATEADD(DAY, -DATEPART(Weekday, Fechas.Fecha), Fechas.Fecha) ))*10000

        +MONTH(DATEADD(DAY, 1, DATEADD(DAY, -DATEPART(Weekday, Fechas.Fecha), Fechas.Fecha) ))*100

        +DAY(DATEADD(DAY, 1, DATEADD(DAY, -DATEPART(Weekday, Fechas.Fecha), Fechas.Fecha) ))

        >YEAR(Fechas.Fecha)*10000+101 THEN

            YEAR(DATEADD(DAY, 1, DATEADD(DAY, -DATEPART(Weekday, Fechas.Fecha), Fechas.Fecha) ))*10000

            +MONTH(DATEADD(DAY, 1, DATEADD(DAY, -DATEPART(Weekday, Fechas.Fecha), Fechas.Fecha) ))*100

            +DAY(DATEADD(DAY, 1, DATEADD(DAY, -DATEPART(Weekday, Fechas.Fecha), Fechas.Fecha) ))

        ELSE YEAR(Fechas.Fecha)*10000+101 END AS SemanaId

    , 'S'+RIGHT('0'+CAST(DATENAME(Week, Fechas.Fecha) AS VARCHAR(25)),2)+ '-' + CAST(Year(Fechas.Fecha) AS CHAR(4)) AS SemanaName

    , DATEPART(Weekday, Fechas.Fecha) AS DiaSemanaID

    , DATENAME(Weekday, Fechas.Fecha) AS DiaSemanaNombre

    , CASE

     WHEN DATEPART(Weekday, Fechas.Fecha) IN (6,7) THEN 'FinSemana'

            ELSE 'EntreSemana'

        END AS DiaEntreFinSemana

    , CASE

        WHEN YEAR(Fechas.Fecha)<1991 THEN 'No Aplica'

        WHEN DATEPART(Weekday, Fechas.Fecha) IN (7) THEN 'NO Hábil'

        WHEN Fechas.Fecha BETWEEN DATEADD(day, -2, dbo.Pascua(YEAR(Fechas.Fecha)))

                        AND dbo.Pascua(YEAR(Fechas.Fecha)) THEN 'NO Hábil'

        WHEN (Fechas.DimFechaId%10000)=1201    -- Primero de Diciembre

                    AND ((Fechas.DimFechaId/10000)-1994)%6=0    -- 1 de Cada 6 años

                    THEN 'NO Hábil'

        WHEN Festividades.Festivo='Festivo Obligatorio'

            AND (Festividades.TipoFeriado='Fijo' or YEAR(Fechas.Fecha)<=2006) -- Nueva Ley Aplica del 2006 en Adelante

            THEN 'NO Hábil'

        WHEN FLunes.Festivo='Festivo Obligatorio'

            AND DATEPART(Weekday, Fechas.Fecha) =1

            THEN 'NO Hábil'

        WHEN FLunes.Festivo='Festivo Obligatorio'

                AND Festividades.TipoFeriado='Pasar'

                AND YEAR(Fechas.Fecha)>2006

            THEN 'NO Hábil'

        ELSE 'Hábil'

        END AS DiaHabilFeriadoCorp

    , CASE

            WHEN YEAR(Fechas.Fecha)<1991 THEN 'No Aplica'    

            WHEN dbo.Pascua(YEAR(Fechas.Fecha))=Fechas.Fecha THEN 'Pascua'

            WHEN Fechas.Fecha BETWEEN DATEADD(day, -1, dbo.Pascua(YEAR(Fechas.Fecha)))

                            AND DATEADD(day, -3, dbo.Pascua(YEAR(Fechas.Fecha)))

                                THEN DATENAME(Weekday, Fechas.Fecha) + ' Santo'

            WHEN (Fechas.DimFechaId%10000)=1201    -- Primero de Diciembre

                        AND ((Fechas.DimFechaId/10000)-1994)%6=0    -- 1 de Cada 6 años

                        THEN 'Transmisión del Poder Ejecutivo Federal'

            ELSE COALESCE(Festividades.Festividad

                                        , FLunes.Festividad

                                        , 'NO Festivo') END AS Festividad

        , CASE

            WHEN YEAR(Fechas.Fecha)<1991 THEN 'No Aplica'    

            WHEN Fechas.Fecha BETWEEN DATEADD(day, -2, dbo.Pascua(YEAR(Fechas.Fecha)))

                                                                                     AND dbo.Pascua(YEAR(Fechas.Fecha)) THEN 'Festivo Obligatorio'        

            WHEN (Fechas.DimFechaId%10000)=1201    -- Primero de Diciembre

                        AND ((Fechas.DimFechaId/10000)-1994)%6=0    -- 1 de Cada 6 años

                                THEN 'Festivo Obligatorio'    

            WHEN Festividades.Festivo IS NOT NULL and Festividades.TipoFeriado='Pasar'

                    THEN 'Festivo Pasado'

            WHEN Festividades.Festivo IS NOT NULL

                    THEN Festividades.Festivo

            WHEN FLunes.Festivo IS NOT NULL

                    THEN 'Lunes: Festivo Pasado'

            ELSE 'NO Festivo' END AS TipoFestivoFestivo

FROM Fechas

LEFT JOIN Festividades

ON (Fechas.DimFechaId % 10000)=Festividades.Dia

    AND YEAR(Fechas.Fecha)>=1990

LEFT JOIN Festividades AS FLunes

ON DATEPART(Weekday, Fechas.Fecha)=1                    -- Dia Lunes

        AND FLunes.TipoFeriado='Pasar'                    -- Que el Feriado sea Pasar

        AND FLunes.Dia/100=month(Fechas.Fecha)        -- Que sea el mismo Mes

        AND YEAR(Fechas.Fecha)>2006

        AND DATEADD(DAY

                , ((8-DATEPART(WEEKDAY, CAST(CAST(YEAR(Fechas.Fecha)*10000+(FLunes.Dia/100)*100+1 AS CHAR(8)) AS SMALLDATETIME)))%7)

                 + ((FLunes.Lunes-1)*7)

                , CAST(CAST(YEAR(Fechas.Fecha)*10000+(FLunes.Dia/100)*100+1 AS CHAR(8)) AS SMALLDATETIME))

                =Fechas.Fecha

        AND DATEPART(Weekday

            , CAST(CAST( FLunes.Dia+10000*YEAR(Fechas.Fecha) as varchar(8)) AS SMALLDATETIME))

            NOT IN (1, 6,7)

OPTION (MAXRECURSION 0);

 

Llenar Dimensión de Fechas en México

El siguiente código lo genere para que mis amigos del Nacional Monte de Piedad y tiene la lógica necesaria para generar un calendario de fechas desde 1901 hasta 2020, incluyendo los feriados obligatorios y no obligatorios de ley (Día de todos los Santos, Día de Muertos, Aniversario de la Batalla Puebla, Aniversario de la Revolución Mexicana, Natalicio de Benito Juarez, Navidad, Día de la Raza, Día de la Bandera, Día de Independencia, Día del Trabajo, Año Nuevo, Día de la Virgen de Guadalupe, Día de la Madre, Aniversario de la Constitución), semana santa (jueves santo, viernes santo, sábado santo y pascua), y el día del traslado del poder ejecutivo de ley.

El código se complica por:

  1. Desde año 2006 algunos de los feriados son fijos y otros se pasan a un lunes específico dentro del mes, por ejemplo el tercer lunes de noviembre.
  2. Los cálculos para la semana santa, son complejos.
  3. La transmisión del Poder Ejecutivo ocurre cada seis años en una fecha fija.

El código puede ser limpiado, para ser simplificado y probablemente requiere ser más comentado, NUEVA VERSION EN: http://blogs.solidq.com/ES/jloria/Lists/Posts/Post.aspx?ID=27:

SET LANGUAGE Spanish;

GO

CREATE FUNCTION dbo.Pascua (@Yr as int)

RETURNS DATETIME

AS

BEGIN

--    Codigo encontrado en: http://www.tek-tips.com/faqs.cfm?fid=5075

Declare @Cent int, @I int, @J int, @K int, @Metonic int, @EMo int, @EDay int

Set @Cent=@Yr/100

Set @Metonic=@Yr % 19

Set @K=(@Cent-17)/25

Set @I=(@Cent-@Cent/4-(@Cent-@K)/3+19*@Metonic+15) % 30

Set @I=@I-(@I/28)*(1-(@I/28)*(29/(@I+1))*((21-@Metonic)/11))

Set @J=(@Yr+@Yr/4+@I+2-@Cent+@Cent/4) % 7

Set @EMo=3+(@I-@J+40)/44

Set @EDay=@I-@J+28-31*(@EMo/4)

RETURN CAST(CAST(@Yr*10000+@Emo*100+@Eday AS VARCHAR(8)) AS DATETIME);

/*This algorithm is from the work done by JM Oudin in 1940 and is accurate from year 1754 to 3400.*/

END;

GO

-- Codigo para llenar fecha de dimension Fecha en Mexico.

; WITH Fechas(Fecha, DimFechaId) AS

(

SELECT CAST('19010101' AS DATETIME)

    , YEAR(CAST('19010101' AS DATETIME))*10000

    +MONTH(CAST('19010101' AS DATETIME))*100

     +DAY(CAST('19010101' AS DATETIME))

UNION ALL

    SELECT DATEADD(DAY, 1, Fecha), YEAR(DATEADD(DAY, 1, Fecha))*10000+MONTH(DATEADD(DAY, 1, Fecha))*100+DAY(DATEADD(DAY, 1, Fecha))

    FROM Fechas

    WHERE DATEADD(DAY, 1, Fecha)<'20210101'

)

, Festividades (Festividad, Festivo, TipoFeriado, Dia, Lunes)

AS( SELECT 'Dia de todos los Santos' ,'Festivo NO Obligatorio', 'NoAplica',    1101, 0

UNION ALL SELECT 'Dia de Muertos' ,'Festivo NO Obligatorio', 'NoAplica    ',    1102, 0

UNION ALL SELECT 'Aniversario de la Batalla Puebla' ,'Festivo NO Obligatorio', 'NoAplica',        505, 0

UNION ALL SELECT 'Aniversario de la Revolución Mexicana' ,'Festivo Obligatorio', 'Pasar',    1120, 3

    -- 3er Lunes Noviembre

UNION ALL SELECT 'Natalicio de Benito Juarez' ,'Festivo Obligatorio', 'Pasar',        321, 3

    -- 3er Lunes Marzo

UNION ALL SELECT 'Navidad', 'Festivo Obligatorio' ,'Fijo', 1225, 0

UNION ALL SELECT 'Dia de la Raza' ,'Festivo NO Obligatorio', 'NoAplica',    1012, 0

UNION ALL SELECT 'Dia de la Bandera' ,'Festivo NO Obligatorio', 'NoAplica', 224, 0

UNION ALL SELECT 'Dia de Independencia' ,'Festivo Obligatorio', 'Fijo', 916, 0

UNION ALL SELECT 'Dia del Trabajo', 'Festivo Obligatorio' ,'Fijo', 501, 0

UNION ALL SELECT 'Año Nuevo' ,'Festivo Obligatorio', 'Fijo',     101, 0

UNION ALL SELECT 'Día de la Virgen de Guadalupe' ,'Festivo Obligatorio', 'Fijo', 1212, 0

UNION ALL SELECT 'Dia de la Madre' ,'Festivo NO Obligatorio', 'NoAplica', 510, 0

UNION ALL SELECT 'Aniversario de la Constitucion' ,'Festivo Obligatorio', 'Pasar', 205, 1

    -- Primer Lunes de Febrero

)

SELECT DimFechaId

    , Fechas.Fecha AS Fecha

    , (DimFechaId/10000)*10000+101 AS AnioId

    , CAST(Year(Fechas.Fecha) AS CHAR(4)) AS AnioNombre

    , 300*(DimFechaId/300)+1 AS TrimestreId

    , 'Q'+CAST(DATEPART(Quarter, Fechas.Fecha) AS VARCHAR(1))

        + '-' +CAST(Year(Fechas.Fecha) AS CHAR(4)) AS TrimestreNombre

    , (DimFechaId/100)*100+1 AS MesId

    , CAST(DATENAME(Month, Fechas.Fecha) AS VARCHAR(25))+ '-'

+ CAST(Year(Fechas.Fecha) AS CHAR(4)) AS MesNombre

     , YEAR(DATEADD(DAY, 1, DATEADD(DAY, -DATEPART(Weekday, Fechas.Fecha), Fechas.Fecha) ))*10000

        +MONTH(DATEADD(DAY, 1, DATEADD(DAY, -DATEPART(Weekday, Fechas.Fecha), Fechas.Fecha) ))*100

        +DAY(DATEADD(DAY, 1, DATEADD(DAY, -DATEPART(Weekday, Fechas.Fecha), Fechas.Fecha) )) AS SemanaId

    , 'S'+RIGHT('0'+CAST(DATENAME(Week, Fechas.Fecha) AS VARCHAR(25)),2)+ '-'

+ CAST(Year(Fechas.Fecha) AS CHAR(4)) AS SemanaName

    , DATEPART(Weekday, Fechas.Fecha) AS DiaSemanaID

    , DATENAME(Weekday, Fechas.Fecha) AS DiaSemanaNombre

    , CASE

     WHEN DATEPART(Weekday, Fechas.Fecha) IN (6,7) THEN 'FinSemana'

            ELSE 'EntreSemana'

        END AS DiaEntreFinSemana

    , CASE

        WHEN YEAR(Fechas.Fecha)<1991 THEN 'No Aplica'

        WHEN DATEPART(Weekday, Fechas.Fecha) IN (7) THEN 'NO Hábil'

        WHEN Fechas.Fecha BETWEEN DATEADD(day, -2, dbo.Pascua(YEAR(Fechas.Fecha)))

                        AND dbo.Pascua(YEAR(Fechas.Fecha)) THEN 'NO Hábil'

        WHEN (Fechas.DimFechaId%10000)=1201    -- Primero de Diciembre

                    AND ((Fechas.DimFechaId/10000)-1994)%6=0    -- 1 de Cada 6 años

                    THEN 'NO Hábil'

        WHEN Festividades.Festivo='Festivo Obligatorio'

            AND (Festividades.TipoFeriado='Fijo' or YEAR(Fechas.Fecha)<=2006)

-- Nueva Ley Aplica del 2006 en Adelante

            THEN 'NO Hábil'

        WHEN FLunes.Festivo='Festivo Obligatorio'

            AND DATEPART(Weekday, Fechas.Fecha) =1

            THEN 'NO Hábil'

        WHEN FLunes.Festivo='Festivo Obligatorio'

                AND Festividades.TipoFeriado='Pasar'

                AND YEAR(Fechas.Fecha)>2006

            THEN 'NO Hábil'

        ELSE 'Hábil'

        END AS DiaHabilFeriadoCorp

    , CASE

            WHEN YEAR(Fechas.Fecha)<1991 THEN 'No Aplica'    

            WHEN dbo.Pascua(YEAR(Fechas.Fecha))=Fechas.Fecha THEN 'Pascua'

            WHEN Fechas.Fecha BETWEEN DATEADD(day, -1, dbo.Pascua(YEAR(Fechas.Fecha)))

                            AND DATEADD(day, -3, dbo.Pascua(YEAR(Fechas.Fecha)))

                                THEN DATENAME(Weekday, Fechas.Fecha) + ' Santo'

            WHEN (Fechas.DimFechaId%10000)=1201    -- Primero de Diciembre

                        AND ((Fechas.DimFechaId/10000)-1994)%6=0    -- 1 de Cada 6 años

                        THEN 'Transmisión del Poder Ejecutivo Federal'

            ELSE COALESCE(Festividades.Festividad

                                        , FLunes.Festividad

                                        , 'NO Festivo') END AS Festividad

        , CASE

            WHEN YEAR(Fechas.Fecha)<1991 THEN 'No Aplica'    

            WHEN Fechas.Fecha BETWEEN DATEADD(day, -2, dbo.Pascua(YEAR(Fechas.Fecha)))

                                                                                     AND dbo.Pascua(YEAR(Fechas.Fecha)) THEN 'Festivo Obligatorio'        

            WHEN (Fechas.DimFechaId%10000)=1201    -- Primero de Diciembre

                        AND ((Fechas.DimFechaId/10000)-1994)%6=0    -- 1 de Cada 6 años

                                THEN 'Festivo Obligatorio'    

            WHEN Festividades.Festivo IS NOT NULL and Festividades.TipoFeriado='Pasar'

                    THEN 'Festivo Pasado'

            WHEN Festividades.Festivo IS NOT NULL

                    THEN Festividades.Festivo

            WHEN FLunes.Festivo IS NOT NULL

                    THEN 'Lunes: Festivo Pasado'

            ELSE 'NO Festivo' END AS TipoFestivoFestivo

FROM Fechas

LEFT JOIN Festividades

ON (Fechas.DimFechaId % 10000)=Festividades.Dia

    AND YEAR(Fechas.Fecha)>=1990

LEFT JOIN Festividades AS FLunes

ON DATEPART(Weekday, Fechas.Fecha)=1                -- Dia Lunes

        AND FLunes.TipoFeriado='Pasar'                -- Que el Feriado sea Pasar

        AND FLunes.Dia/100=month(Fechas.Fecha)        -- Que sea el mismo Mes

        AND YEAR(Fechas.Fecha)>2006

        AND DATEADD(DAY, ((8-DATEPART(WEEKDAY, CAST(CAST(YEAR(Fechas.Fecha)*10000

+(FLunes.Dia/100)*100+1 AS CHAR(8)) AS SMALLDATETIME)))%7)+ ((FLunes.Lunes-1)*7)

            , CAST(CAST(YEAR(Fechas.Fecha)*10000+(FLunes.Dia/100)*100+1 AS CHAR(8)) AS SMALLDATETIME))

                =Fechas.Fecha

        AND DATEPART(Weekday

            , CAST(CAST( FLunes.Dia+10000*YEAR(Fechas.Fecha) as varchar(8)) AS SMALLDATETIME))

            NOT IN (1, 6,7)

ORDER BY Fechas.Fecha

OPTION (MAXRECURSION 0)

 

Definitivamente el lenguaje SQL no fue creado para hacer este tipo de consultas!

OPIs (Indicadores de Desempeño Operacional): Parte 1

Un tema recurrente en la teoría de inteligencia de negocios son los indicadores claves de desempeño o KPIs por sus siglas en ingles (Key Performance Indicators). Los KPIs son métricas que las empresas u organizaciones usan para medir el cumplimiento o avance de objetivos estratégicos.

Cuando se emplean dentro de un contexto de cuadros o tableros de mando balanceado, y usando la metodología de Kaplan y Norton se construyen en 4 etapas, de un proceso cíclico:

  1. Trasladar la visión a objetivos operativos.
  2. Comunicar la visión y enlazar la visión con el desempeño individual (Fijar Objetivos)
  3. Planeamiento de negocios (Fijar metas).
  4. Retroalimentación y aprendizaje

Diagrama de Proceso de Kaplan y Norton:

Es claro que este proceso se sale del control de los departamentos de tecnología y que debe ser promovido y regulado por la dirección general de la empresa. A pesar de esto, de forma recurrente me encuentro que muchas organizaciones quieren implementar una estrategia de KPIs y de cuadros de mando sin tomar las consideraciones estratégicas que implica, sin involucrar a los ejecutivos o dirección general de la empresa y más importante sin tener la madurez de inteligencia de negocios necesaria.

Es muy opinión que la implementación de cuadros de mando de forma prematura, cuando la organización no está suficientemente madura y sin realizar los procesos necesarios para madurar, produce efectos destructivos para la organización.

Por eso creo que es necesaria la construcción de un "nuevo término" que le permitan a las organizaciones madurar en inteligencia de negocios y facilitar la instalación de una infraestructura tecnológica que luego puede ser usada cuando la dirección requiera cuadros de mando estratégicos y KPIs.

Mas sobre la necesidad de OPIs, en el próximo posteo http://blogs.solidq.com/ES/jloria/Lists/Posts/Post.aspx?ID=30.

Consulta para generar rangos

En ciertas ocasiones es necesario extraer de una tabla cuales son los números "continuos" de Facturas o de números de documentos. Esto es útil particularmente por razones de auditoría y de BI. La siguiente consulta realiza dicha operación:

SELECT Principio.NumeroFactura, MIN(final.NumeroFactura)
FROM (SELECT n1.NumeroFactura
FROM dbo.Facturas AS n1
LEFT JOIN dbo.Facturas AS n2
ON n1.NumeroFactura=n2.NumeroFactura+1
WHERE n2.NumeroFactura IS NULL) AS Principio
JOIN (SELECT n1.NumeroFactura
FROM dbo.Facturas AS n1
LEFT JOIN dbo.Facturas AS n2
ON n1.NumeroFactura=n2.NumeroFactura-1
WHERE n2.NumeroFactura IS NULL) AS Final
ON Principio.NumeroFactura <=Final.NumeroFactura
GROUP BY Principio.NumeroFactura

La consulta puede ser intimidante al principio pero en realidad solo usa dos técnicas básicas. La primera técnica puede encontrarse en las sub-consultas internas. Analice la sub-consulta:

SELECT n1.NumeroFactura
FROM dbo.Facturas AS n1
LEFT JOIN dbo.Facturas AS n2
ON n1.NumeroFactura=n2.NumeroFactura+1
WHERE n2.NumeroFactura IS NULL

Se puede ver que usa la combinación de un LEFT JOIN de la tabla con ella misma y un IS NULL para producir los inicios de bloques de facturas. Note que el ON usa NumeroFactura+1 o sea traducido de SQL a español podría ser: liste los NumeroFactura que no tienen (IS NULL) un número anterior consecutivo (ON n1.NumeroFactura=2.NumeroFactura+1). Esta técnica ser repite en la otra subconsulta pero usando un -1, por lo que obtiene los números que no tiene números posteriores consecutivos.

La segunda técnica se emplea para unir estas dos consultas, ya que no tiene ningún elemento identificador en común, por lo que la técnica básicamente une la factura inicial del rango escogiendo el Final del bloque de facturas como la más pequeña de las facturas finales que sea mayor o igual a el mismo. Eliminando las sub-consultas puede leerse:

SELECT Principio.NumeroFactura, MIN(final.NumeroFactura)
FROM Principio
JOIN Final
ON Principio.NumeroFactura <=Final.NumeroFactura
GROUP BY Principio.NumeroFactura

Esta consulta funciona en cualquier versión de SQL (7.0, 2000, 2005 y 2008). Pero en 2005 y 2008 puede hacerse más legible cuando se usan CTEs:

;WITH Principio AS (

    SELECT n1.NumeroFactura

    FROM dbo.Facturas AS n1

    LEFT JOIN dbo.Facturas AS n2

    ON n1.NumeroFactura=n2.NumeroFactura+1

    WHERE n2.NumeroFactura IS NULL)

, Final AS (

    SELECT n1.NumeroFactura

    FROM dbo.Facturas AS n1

    LEFT JOIN dbo.Facturas AS n2

    ON n1.NumeroFactura=n2.NumeroFactura-1

    WHERE n2.NumeroFactura IS NULL)

SELECT Principio.NumeroFactura, MIN(final.NumeroFactura)

FROM Principio

JOIN Final

ON Principio.NumeroFactura <=Final.NumeroFactura

GROUP BY Principio.NumeroFactura

Optimización Automática: Desfragmentación de Índices SQL 2005/2008 V2

Esta es una versión mejorada del procedimiento automático de desfragmentación de índices, que usa una tabla para almacenar el resultado de la consulta y así evita usar una y otra vez dm_db_index_physical_stats que tiene serios problemas de rendimiento. El código original puede verse en: http://blogs.solidq.com/ES/jloria/Lists/Posts/Post.aspx?ID=20.

USE <BaseDatos>;

GO

CREATE FUNCTION FilteredIndexFragmentation(

     @DatabaseID                INT

    , @ObjectID                    INT

    , @IndexID                        INT

    , @PartitionNumber        INT=NULL

    , @AverageFragmentation INT =0

    , @FragmentCount        BIGINT =0)

-- Author:                         Javier Loria, Solid Quality Mentors

-- Create date:                    5/Dic/2008

-- Description:                    Funcion que lista los indices con un porcentaje de fragmentacion LOGICA mayor al indicado,

-- y con una cantidad mayor de fragmentos.

-- Encapsula dm_db_index_physical_stats., se requiere para poder hacer CROSS APPLY.

-- No reporta fragmentacion de tablas sin indices, indices XML o Geograficos.

-- Emplea el modo limitado 'LIMITED', por el alto costo y mal desempeno del modo 'DETAILED'

RETURNS @IndexStats TABLE(

     DatabaseID                    SMALLINT

    , ObjectID                        INT

    , IndexID                            INT

    , PartitionNumber            INT

    , IndexDepth                    TINYINT

    , FragmentationRate        FLOAT

    , FragmentCount            BIGINT

    , AverageFragmentSize FLOAT

    , PageCount                    BIGINT)

BEGIN

    INSERT INTO @IndexStats(DatabaseID, ObjectID, IndexID, PartitionNumber, IndexDepth,FragmentationRate

        , FragmentCount, AverageFragmentSize, PageCount)

    SELECT database_id, object_id, index_id, partition_number,

         index_depth, avg_fragmentation_in_percent, fragment_count, avg_fragment_size_in_pages, page_count

    FROM sys.dm_db_index_physical_stats (@DatabaseID, @ObjectID, @IndexID, @PartitionNumber, 'LIMITED' )

    WHERE index_type_desc IN('CLUSTERED INDEX', 'NONCLUSTERED INDEX')

    AND avg_fragmentation_in_percent > @AverageFragmentation

    AND fragment_count>@FragmentCount

RETURN

END

GO

 

CREATE TABLE dbo.FragmentacionIndices(

     Name sysname        NOT NULL

        PRIMARY KEY

    , Indice varchar(512)    NOT NULL

    , Tamano varchar(8)        NOT NULL

    , TotalPages bigint        NOT NULL

);

GO

CREATE PROCEDURE [dbo].[DefragmentaIndices](

@Tipo VARCHAR(10)='Grandes' -- Medianas, Pequenas, Genera

)

AS

DECLARE @db_id INT;

DECLARE @NumPages BIGINT;

DECLARE @NumIndexes INT;

DECLARE @Comando NVARCHAR(MAX);

DECLARE @DB INT

-- Parametros "ALAMBRADOS"

    -- +8192 Paginas: Grande

    -- +256 Paginas: Mediano

    -- -256 Paginas: Pequeno

-- -32 Paginas: No Defragmenta

-- La tabla se llena con las 400 Tablas + Grandes

    -- Grandes: Defragmenta 10, Medianas: Defragmenta 10, Pequenas: Defragmanta 100

SET NOCOUNT ON;

SET @DB=DB_ID() --Requerido por modo de compatibilidad 80.

 

IF (@Tipo NOT IN('Grandes', 'Medianas', 'Pequenas', 'Genera'))

     BEGIN

     RAISERROR('Parametro @Tipo Invalido, use: Grandes, Medianas o Pequenas', 16,1);

     RETURN;

     END

SET @db_id = DB_ID();

SET @Comando='';

IF @Tipo='Genera'

    BEGIN

    TRUNCATE TABLE dbo.FragmentacionIndices;

    INSERT INTO dbo.FragmentacionIndices(Name, Indice, Tamano, TotalPages)

    SELECT TOP 400

        IndexPages.Name

        , 'ALTER INDEX '

        + IndexPages.Name

        +' ON '+OBJECT_NAME(ObjectID)+' REBUILD;' AS Indice

        , CASE WHEN totalPages>=8192 THEN 'Grandes'

             WHEN totalPages>=256 THEN 'Medianas'

             ELSE 'Pequenas' END AS Tamano

        , TotalPages

    FROM (SELECT indexes.object_id

    , indexes.index_id

    , Indexes.Name

    , sum(allocation_units.total_pages) as totalPages

    FROM sys.indexes AS indexes

    JOIN sys.partitions AS partitions

    ON indexes.object_id = partitions.object_id

     and indexes.index_id = partitions.index_id

    JOIN sys.allocation_units AS allocation_units

    ON partitions.partition_id = allocation_units.container_id

    WHERE indexes.index_id >0

     AND allocation_units.total_pages>0

    GROUP BY indexes.object_id, indexes.index_id, Indexes.Name

    HAVING sum(allocation_units.total_pages)> 32) AS IndexPages

    -- CROSS APPLY FilteredIndexFragmentation(@DB, IndexPages.object_id, IndexPages.index_id, NULL, 20,3) AS FIF

    JOIN FilteredIndexFragmentation(@DB, NULL, NULL, NULL, 20,3) AS FIF

    ON IndexPages.object_id=FIF.ObjectID

    AND IndexPages.index_id=FIF.IndexID

    ORDER BY (IndexDepth*IndexDepth*FragmentationRate*FragmentCount/100) DESC;

    RETURN;

    END

 

IF @Tipo='Grandes'

BEGIN

-- Reindexa las 10 mas grandes sin importar el tamano

    SET @Comando=''

    SELECT TOP 10 @Comando=@Comando+CHAR(13)+CHAR(10)+Indice

    FROM dbo.FragmentacionIndices

    ORDER BY TotalPages DESC;

    -- Borra

    DELETE dbo.FragmentacionIndices

FROM dbo.FragmentacionIndices

JOIN (SELECT TOP 10 Name FROM dbo.FragmentacionIndices

            ORDER BY TotalPages DESC) AS A

    ON FragmentacionIndices.Name=A.Name

END

IF @Tipo='Medianas'

BEGIN

-- Reindexa las 10 mas grandes, que no sean "Grandes" (Medianas, Pequenas)

    SET @Comando=''

    SELECT TOP 10 @Comando=@Comando+CHAR(13)+CHAR(10)+Indice

    FROM dbo.FragmentacionIndices

    WHERE Tamano<>'Grandes'

    ORDER BY TotalPages DESC;

    DELETE dbo.FragmentacionIndices

FROM dbo.FragmentacionIndices

JOIN (SELECT TOP 10 Name

            FROM dbo.FragmentacionIndices

            WHERE Tamano<>'Grandes'

            ORDER BY TotalPages DESC) AS A

    ON FragmentacionIndices.Name=A.Name

 

END

IF @Tipo='Pequenas'

BEGIN

-- Reindexa las 100 Pequenas + Grandes

    SET @Comando=''

    SELECT TOP 100 @Comando=@Comando+CHAR(13)+CHAR(10)+Indice

    FROM dbo.FragmentacionIndices

    WHERE Tamano='Pequenas'

    ORDER BY TotalPages DESC;

    DELETE dbo.FragmentacionIndices

FROM dbo.FragmentacionIndices

JOIN (SELECT TOP 100 Name

            FROM dbo.FragmentacionIndices

            WHERE Tamano='Pequenas'

            ORDER BY TotalPages DESC) AS A

    ON FragmentacionIndices.Name=A.Name

END

EXEC sp_executesql @Comando

GO

/*

-- Pruebas

EXEC [dbo].[DefragmentaIndices] 'Genera'

SELECT * FROM dbo.FragmentacionIndices

EXEC [dbo].[DefragmentaIndices] 'Grandes'

EXEC [dbo].[DefragmentaIndices] 'Medianas'

EXEC [dbo].[DefragmentaIndices] 'Pequenas'

--

*/

 

SELECT sin FROM

En el ambiente de SQL de Microsoft no hay problema de hacer un SELECT sin usar FROM. Por ejemplo es lícito hacer:

Microsoft

SELECT 'Prueba'

Pero en otras plataformas, un SELECT sin FROM es sintaxis inválida.

Para lograr el mismo objetivo en Oracle puede usarse la tabla Dual, y en DB2 sysibm.sysdummy1:

Oracle:

SELECT 'Prueba' FROM DUAL

DB2

SELECT 'Prueba' FROM sysibm.sysdummy1

1 - 10 Next

 ‭(Hidden)‬ Admin Links


¿Problemas técnicos? Contáctenos en webmaster@solidq.com o al 800 300 800 (desde España) o al +34 91 414 8950 (desde fuera de España)