No se puede crear el índice porque la columna de clave 'DiaLlamada' no es determin ista o es imprecisa.

02/02/2009 - 22:08 por Mauro Pasetti | Informe spam
Necesito optimizar la performance para devolver registros de una tabla
para una fecha determinada (sin importar el horario).
Utilizo SQL Server 2000 EE

Utilicé DateAdd(day,0,FecHoraLogin)
Utilicé DateAdd(day,cast(0 as datetime),FecHoraLogin)
y finalemente...

[DiaLlamada] AS (convert(int,[FecHoraLogin]))

Por que quiero crear el siguiente índice:
CREATE NONCLUSTERED INDEX [Key_DiaLlamada] ON
[dbo].[MECResumenDiarioTiempos]
(
DiaLlamada,
IdAgente
) ON [SECONDARY]
GO
pero siempre tengo el mismo error.
No se puede crear el índice porque la columna de clave 'DiaLlamada' no
es determinista o es imprecisa.

Agradecería cualquier consejo y/o ayuda porque por toda la info que
busqué, no encontré nada que me pueda ayudar. Tampo conozco si existe
otra forma mejor para obtener la performance que busco.

Preguntas similare

Leer las respuestas

#6 Jose Mariano Alvarez
03/02/2009 - 13:41 | Informe spam
Mauro:

Te comento mi experiencia al respecto. He manejado tablas de cientos de
millones de registros de esa manera y nunca me provocado inconvenientes. En
algunos casos los indices sobre columnas calculadas me han generado errores
"access violation" (especialmente en SQLServer 2000) dependiendo de la
complejidad de la consulta.

Te sugiero que revises y compares las estadisticas y muy especialmente las
densidades de tu indice y del indice por fecha que te propongo y veras lo
diferentes que son. En un volumen grande de registros con uso de stored
procedures veras mas beneficios si las sentencias estan escritas como te
dije. Bajo otras circunstancias son practicamente equivalentes los dos y el
de fecha es mejor si usas smalldateime porque utiliza la mitad de espacio de
almacenamiento.

En el caso de 4M de registros/10K por dia tienes 400 dias aproximadamente lo
que te deja una densidad aproximada de primer nivel de 1/400. Luego si
aplicas una condicion por "=fecha" (que es mucho mejor que el dadediff) y
ademas precisas datos que no estan en el indice por lo que es necesario un
acceso a la pagina de datos (suponiendo que tengas un clustered index)
deberas acceder haciendo al menos el acceso al indice secundario (SCAN o
SEEK) y luego 10000 accesos por indice clustered a la tabla. En cualquier
caso no creo que el indice secundario sea determinante en el costo total
sino el acceso mediante el clustered index. Si precisas los datos de parte
de un dia tu indice no te ayuda tanto como el de fecha que te comente..

En el caso del uso del datediff es casi seguro que por la estructura de tu
consulta veras un INDEXSCAN en lugar de un SEEK.



Saludos


Ing. Jose Mariano Alvarez
SQLTotal Consulting

(Cambia los ceros por O y saca lo que sobra)

Este mensaje se proporciona tal como es, SIN GARANTIAS de ninguna clase. Por
favor tratar de indicar la versión de SQL y Service Pack. La inclusión de
(CREATE, INSERTS, etc.) para poder reproducir el problema también ayuda.










"Mauro Pasetti" wrote in message
news:OFP#
Estimado José, actualmente yo utilizo DateDiff que es mucho más simple
para buscar datos de una fecha sobre un campo FechaHora y es específico
para esta tarea (y existe un índice para tal caso), pero cuando hablamos
de 4.000.000 de registros y 10.000 registros por día, pienso que me
optimiza mucho más un índice por un campo con un valor único por día.

Salu2, Mauro

Jose Mariano Alvarez escribió:
1 Cea un indice sobre la columna fecha (con hora)
2 Corrige la consulta para que el where diga

WHERE fecha >= @dia
and fecha < dateadd(dd,1,@dia)


Respuesta Responder a este mensaje
#7 Alejandro Mesa
03/02/2009 - 15:09 | Informe spam
Mauro Pasetti,

Siendo IdAgente un entero, me pregunto si no me conviene hacer:
[DiaLLamada] as (convert(int,convert(char(8),[FecHoraLogin],112)))



Si usas tipo de data entero, la ventaja estaria en que este tipo ocupa menos
bytes (4) comparado con los 8 bytes de la cadena yyyymmdd. Esto hace que
podamos tener mas registros por paginas, que a la final se traduce en menos
lectura de disco.

declare @DiaLlamada int
declare @IdAgente

set @DiaLlamada = convert(char(8), getdate(), 112)
set @IdAgente = 23

select c1, ..., cn
from t
where DiaLlamada = @DiaLlamada and IdAgente = @IdAgente;

Si el numero de filas que cumplen esta condicion es tal que escanear la
tabla o el indice clustered seria menos costoso, entonces este indice que
creastes no sera usado para traer la data, pero si el numero de filas que
cumplen la condicion es tal que usar este indice para encontrar las filas y
luego ir a la tabla o al indice clustered para traer el resto de la data, es
menos costoso entonces este indice cumplira su tarea para lo que se creo.


AMB

"Mauro Pasetti" wrote:

Excelente Alejandro, gracias. Es un código más simple, ahora me
pregunto si para el índice que tengo que hacer
CREATE NONCLUSTERED INDEX [Key_DiaLlamada] ON
[dbo].[MECResumenDiarioTiempos]
(
DiaLlamada,
IdAgente
) ON [SECONDARY]
GO

Siendo IdAgente un entero, me pregunto si no me conviene hacer:
[DiaLLamada] as (convert(int,convert(char(8),[FecHoraLogin],112)))

Para trabajar un un índice numérico. La verdad es que no conozco mucho
a cerca de la forma en que trabajan los índices de SQL Server y cual
sería la opción más conveniente, si lo sabés y me desburrás, te lo
agradecería.

Salu2, Mauro.

Alejandro Mesa escribió:
> Mauro,
>
> Trata:
>
> [DiaLLamada] as convert(char(8), FecHoraLogin, 112)
>
>
>
> AMB
>
>
>
> "Mauro Pasetti" wrote:
>
>> ya lo solucioné, agradecería si otro tiene una propuesta mejor:
>>
>> (datepart(year,[FecHoraLogin]) * 10000 + datepart(month,[FecHoraLogin])
>> * 100 + datepart(day,[FecHoraLogin]))
>>
>> Es determinista y funciona con el índice.
>>
>> Salu2, y gracias a todos. Si conocen una fórmula mejor, por favor no
>> duden en enviarla.
>>

Respuesta Responder a este mensaje
#8 Alejandro Mesa
03/02/2009 - 15:41 | Informe spam
Mauro Pasetti,

Estimado José, actualmente yo utilizo DateDiff que es mucho más simple
para buscar datos de una fecha sobre un campo FechaHora



Es mas simple su uso, pero tiene el inconveniente de que debes aplicarselo a
la columna por la que quieres hacer la busqueda y esto evita a que SQL Server
pueda usar algun indice existente por esta columna, para estimar el numero de
filas que machan esa condicion, pues las estadisticas de distribucion se
basan en valores de la columna y no en valores resultantes de la funcion que
aplicas sobre ella.

Una de las ventajas que se tiene a partir de la version 2005, es que cuando
creamos una columna calculada y la indexamos, la penalizacion que comente
anteriormente no aplica, osea que si usas la misma funcion que usastes para
crear la columna calculada, sql server todavia puede hacer uso de las
estadisticas sobre esta columna para estimar cardinalidad.

Ejemplo: (SS 2005 EE sp3)

USE Northwind;
GO

ALTER TABLE dbo.Orders
ADD OrderDate_new AS CONVERT(CHAR(8), OrderDate, 112) PERSISTED;
GO

CREATE INDEX Orders_OrderDate_new_nu_nc_ix
ON dbo.Orders(OrderDate_new);
GO

DECLARE @f CHAR(8);

SET @f = '19980226';

SELECT OrderID, OrderDate, CustomerID
FROM dbo.Orders
WHERE CONVERT(CHAR(8), OrderDate, 112) = @f
OPTION (RECOMPILE);

SELECT OrderID, OrderDate, CustomerID
FROM dbo.Orders
WHERE OrderDate >= @f AND OrderDate < DATEADD([day], 1, @f)
OPTION (RECOMPILE);
GO

ALTER TABLE dbo.Orders
DROP COLUMN OrderDate_new;
GO

Asegurate de presionar ctrl-k antes de ejecutar las dos sentencias select.
En el plan de ejecucion veras que el optimizador de queries opta por hacer
uso de indices para estimar cardinalidad. En la primera el indice por
OrderDate_new y la segunda el indice por OrderDate.

Use la opcion recompile para que el optimizador pueda saber los valores de
las variables antes de estimar la cardinalidad.

Inclui la segunda sentencia para reafirmar lo sugerido por Jose Mariano, de
hacer la busqueda para una fecha, sin tomar en cuenta la parte de tiempo,
usando el patron:

columna_tipo_datatime >= @fecha and columna_tipo_datetime < dateadd(day, 1,
@fecha)

Debes estar seguro que el parametro tipo fecha no contiene parte de tiempo.


AMB


"Mauro Pasetti" wrote:

Estimado José, actualmente yo utilizo DateDiff que es mucho más simple
para buscar datos de una fecha sobre un campo FechaHora y es específico
para esta tarea (y existe un índice para tal caso), pero cuando hablamos
de 4.000.000 de registros y 10.000 registros por día, pienso que me
optimiza mucho más un índice por un campo con un valor único por día.

Salu2, Mauro

Jose Mariano Alvarez escribió:
> 1 Cea un indice sobre la columna fecha (con hora)
> 2 Corrige la consulta para que el where diga
>
> WHERE fecha >= @dia
> and fecha < dateadd(dd,1,@dia)
>
>

Respuesta Responder a este mensaje
#9 Alejandro Mesa
03/02/2009 - 15:43 | Informe spam
Me falto eliminar el indice nuevo, antes de eliminar la nueva columna.

DROP INDEX Orders_OrderDate_new_nu_nc_ix
ON dbo.Orders;
GO

ALTER TABLE dbo.Orders
DROP COLUMN OrderDate_new;
GO


AMB

"Alejandro Mesa" wrote:

Mauro Pasetti,

> Siendo IdAgente un entero, me pregunto si no me conviene hacer:
> [DiaLLamada] as (convert(int,convert(char(8),[FecHoraLogin],112)))

Si usas tipo de data entero, la ventaja estaria en que este tipo ocupa menos
bytes (4) comparado con los 8 bytes de la cadena yyyymmdd. Esto hace que
podamos tener mas registros por paginas, que a la final se traduce en menos
lectura de disco.

declare @DiaLlamada int
declare @IdAgente

set @DiaLlamada = convert(char(8), getdate(), 112)
set @IdAgente = 23

select c1, ..., cn
from t
where DiaLlamada = @DiaLlamada and IdAgente = @IdAgente;

Si el numero de filas que cumplen esta condicion es tal que escanear la
tabla o el indice clustered seria menos costoso, entonces este indice que
creastes no sera usado para traer la data, pero si el numero de filas que
cumplen la condicion es tal que usar este indice para encontrar las filas y
luego ir a la tabla o al indice clustered para traer el resto de la data, es
menos costoso entonces este indice cumplira su tarea para lo que se creo.


AMB

"Mauro Pasetti" wrote:

> Excelente Alejandro, gracias. Es un código más simple, ahora me
> pregunto si para el índice que tengo que hacer
> CREATE NONCLUSTERED INDEX [Key_DiaLlamada] ON
> [dbo].[MECResumenDiarioTiempos]
> (
> DiaLlamada,
> IdAgente
> ) ON [SECONDARY]
> GO
>
> Siendo IdAgente un entero, me pregunto si no me conviene hacer:
> [DiaLLamada] as (convert(int,convert(char(8),[FecHoraLogin],112)))
>
> Para trabajar un un índice numérico. La verdad es que no conozco mucho
> a cerca de la forma en que trabajan los índices de SQL Server y cual
> sería la opción más conveniente, si lo sabés y me desburrás, te lo
> agradecería.
>
> Salu2, Mauro.
>
> Alejandro Mesa escribió:
> > Mauro,
> >
> > Trata:
> >
> > [DiaLLamada] as convert(char(8), FecHoraLogin, 112)
> >
> >
> >
> > AMB
> >
> >
> >
> > "Mauro Pasetti" wrote:
> >
> >> ya lo solucioné, agradecería si otro tiene una propuesta mejor:
> >>
> >> (datepart(year,[FecHoraLogin]) * 10000 + datepart(month,[FecHoraLogin])
> >> * 100 + datepart(day,[FecHoraLogin]))
> >>
> >> Es determinista y funciona con el índice.
> >>
> >> Salu2, y gracias a todos. Si conocen una fórmula mejor, por favor no
> >> duden en enviarla.
> >>
>
Respuesta Responder a este mensaje
#10 Alejandro Mesa
03/02/2009 - 15:46 | Informe spam
Este comentario no pertenece a este post, sino al otro donde incorpore un
script de ejemplo.


AMB

"Alejandro Mesa" wrote:

Me falto eliminar el indice nuevo, antes de eliminar la nueva columna.

DROP INDEX Orders_OrderDate_new_nu_nc_ix
ON dbo.Orders;
GO

ALTER TABLE dbo.Orders
DROP COLUMN OrderDate_new;
GO


AMB

"Alejandro Mesa" wrote:

> Mauro Pasetti,
>
> > Siendo IdAgente un entero, me pregunto si no me conviene hacer:
> > [DiaLLamada] as (convert(int,convert(char(8),[FecHoraLogin],112)))
>
> Si usas tipo de data entero, la ventaja estaria en que este tipo ocupa menos
> bytes (4) comparado con los 8 bytes de la cadena yyyymmdd. Esto hace que
> podamos tener mas registros por paginas, que a la final se traduce en menos
> lectura de disco.
>
> declare @DiaLlamada int
> declare @IdAgente
>
> set @DiaLlamada = convert(char(8), getdate(), 112)
> set @IdAgente = 23
>
> select c1, ..., cn
> from t
> where DiaLlamada = @DiaLlamada and IdAgente = @IdAgente;
>
> Si el numero de filas que cumplen esta condicion es tal que escanear la
> tabla o el indice clustered seria menos costoso, entonces este indice que
> creastes no sera usado para traer la data, pero si el numero de filas que
> cumplen la condicion es tal que usar este indice para encontrar las filas y
> luego ir a la tabla o al indice clustered para traer el resto de la data, es
> menos costoso entonces este indice cumplira su tarea para lo que se creo.
>
>
> AMB
>
> "Mauro Pasetti" wrote:
>
> > Excelente Alejandro, gracias. Es un código más simple, ahora me
> > pregunto si para el índice que tengo que hacer
> > CREATE NONCLUSTERED INDEX [Key_DiaLlamada] ON
> > [dbo].[MECResumenDiarioTiempos]
> > (
> > DiaLlamada,
> > IdAgente
> > ) ON [SECONDARY]
> > GO
> >
> > Siendo IdAgente un entero, me pregunto si no me conviene hacer:
> > [DiaLLamada] as (convert(int,convert(char(8),[FecHoraLogin],112)))
> >
> > Para trabajar un un índice numérico. La verdad es que no conozco mucho
> > a cerca de la forma en que trabajan los índices de SQL Server y cual
> > sería la opción más conveniente, si lo sabés y me desburrás, te lo
> > agradecería.
> >
> > Salu2, Mauro.
> >
> > Alejandro Mesa escribió:
> > > Mauro,
> > >
> > > Trata:
> > >
> > > [DiaLLamada] as convert(char(8), FecHoraLogin, 112)
> > >
> > >
> > >
> > > AMB
> > >
> > >
> > >
> > > "Mauro Pasetti" wrote:
> > >
> > >> ya lo solucioné, agradecería si otro tiene una propuesta mejor:
> > >>
> > >> (datepart(year,[FecHoraLogin]) * 10000 + datepart(month,[FecHoraLogin])
> > >> * 100 + datepart(day,[FecHoraLogin]))
> > >>
> > >> Es determinista y funciona con el índice.
> > >>
> > >> Salu2, y gracias a todos. Si conocen una fórmula mejor, por favor no
> > >> duden en enviarla.
> > >>
> >
email Siga el debate Respuesta Responder a este mensaje
Ads by Google
Help Hacer una pregunta AnteriorRespuesta Tengo una respuesta
Search Busqueda sugerida