Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 General SQL Server Forums
 New to SQL Server Programming
 Adding never fullfilled condition speeds up query

Author  Topic 

barnabeck
Posting Yak Master

236 Posts

Posted - 2015-03-12 : 13:33:42
I am facing a totally weird behavior with the execution time of this query.
declare @USERID nvarchar(10)
set @USERID = 39162131

with marcaje as

(SELECT *
from marcaje3
WHERE USERID = @USERID OR @USERID = '*'),

OrderTime as
(SELECT *, ROW_NUMBER() OVER (PARTITION BY USERID, DATEADD(day, DATEDIFF(day, 0, SCHEDULETIME), 0) ORDER BY SCHEDULETIME) AS rn
From marcajes)

SELECT *
FROM OrderTime T1
left outer Join OrderTime T2 on DATEADD(day, DATEDIFF(day, 0, T1.SCHEDULETIME), 0) = DATEADD(day, DATEDIFF(day, 0, T2.SCHEDULETIME), 0) and
T1.USERID = T2.USERID and
T1.rn = T2.rn - 1
I'm using this query in a form, where both a specific USERID or a Wildcard '*' can be entered. Execution time is immediate.

Now, in the detailed view for a specific user (@USERID = '123456') I use the same query but logically with the WHERE clause: WHERE USERID = @USERID
To my surprise this takes 36 seconds to execute! If I add any extra condition, if it is fulfilled or not, execution time is back to 1 second?
Even if I hardcode it WHERE USERID = '123456' it takes the 36 seconds until I add or @USERID = 9999999...

I tried to add the condition or 1 = 2 but this wont work to speed the execution up

Any explanations???

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2015-03-12 : 14:12:24
Compare the execution plans.

Is the code inside a stored procedure? If so, recompile it. Otherwise you can free the procedure cache or drop the plan for the adhoc query.

Tara Kizer
SQL Server MVP since 2007
http://weblogs.sqlteam.com/tarad/
Go to Top of Page

barnabeck
Posting Yak Master

236 Posts

Posted - 2015-03-12 : 18:01:50
The code is just executed in Management Studio but will than be run through a asp:SQLDatasource connection.
1.Query (fast, with dummy added condition: WHERE USERID = 3456789 or @USERID = NOUSERWITHTHISID)

(6 row(s) affected)
Tabla 'HorarioTeorico'. Recuento de exploraciones 200, lecturas lógicas 679, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Semanario_has_Horario'. Recuento de exploraciones 100, lecturas lógicas 479, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Calendario_has_Semanario'. Recuento de exploraciones 193, lecturas lógicas 4632, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Worktable'. Recuento de exploraciones 1, lecturas lógicas 0, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Diario2'. Recuento de exploraciones 1, lecturas lógicas 186000, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Horario_Especial'. Recuento de exploraciones 100, lecturas lógicas 400, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Usuario2_Calendario'. Recuento de exploraciones 100, lecturas lógicas 200, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Incidencia'. Recuento de exploraciones 79, lecturas lógicas 158, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Seccion'. Recuento de exploraciones 198, lecturas lógicas 396, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Usuario2'. Recuento de exploraciones 2, lecturas lógicas 147620, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Marcaje3'. Recuento de exploraciones 122, lecturas lógicas 1480456, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Usuario2_ConaccNis2'. Recuento de exploraciones 122, lecturas lógicas 1098, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Worktable'. Recuento de exploraciones 0, lecturas lógicas 0, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.

Tiempos de ejecución de SQL Server:
Tiempo de CPU = 33337 ms, tiempo transcurrido = 33385 ms.


2.Query (slow, with normal condition: WHERE USERID = 3456789)

(6 row(s) affected)
Tabla 'HorarioTeorico'. Recuento de exploraciones 200, lecturas lógicas 679, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Semanario_has_Horario'. Recuento de exploraciones 100, lecturas lógicas 479, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Worktable'. Recuento de exploraciones 393, lecturas lógicas 491010, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Calendario_has_Semanario'. Recuento de exploraciones 1, lecturas lógicas 24, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Diario2'. Recuento de exploraciones 1, lecturas lógicas 1860, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Horario_Especial'. Recuento de exploraciones 1, lecturas lógicas 4, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Usuario2_Calendario'. Recuento de exploraciones 100, lecturas lógicas 200, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Incidencia'. Recuento de exploraciones 79, lecturas lógicas 158, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Seccion'. Recuento de exploraciones 198, lecturas lógicas 396, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Worktable'. Recuento de exploraciones 0, lecturas lógicas 0, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Usuario2'. Recuento de exploraciones 2, lecturas lógicas 20, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Marcaje3'. Recuento de exploraciones 2, lecturas lógicas 24286, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.
Tabla 'Usuario2_ConaccNis2'. Recuento de exploraciones 2, lecturas lógicas 18, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas de LOB 0, lecturas físicas de LOB 0, lecturas anticipadas de LOB 0.

Tiempos de ejecución de SQL Server:
Tiempo de CPU = 1280 ms, tiempo transcurrido = 1295 ms.


OK, I see that the slow version accesses the marcaje3 table 1.5 million times, while the fast one only 25'000 times. This table most likely can't be indexed, as it is log table where the time clock saves all timestamps. Anyway... why do both queries have different execution plans? They are the same, except the added dummy condition! Can't I force that it is executed in the same faster way that avoids the massive reading of my unindexed table??? Anyway, for my purpose I will just add the dummy expression... it doesn't do any harm to the result, just to my sense of logic.

Martin
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2015-03-12 : 18:05:45
You can force the plan (they are called plan guides), but I wouldn't recommend it. Instead clear the bad plan from cache and then rerun it.

Tara Kizer
SQL Server MVP since 2007
http://weblogs.sqlteam.com/tarad/
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2015-03-12 : 18:06:44
And I would definitely add an index to USERID for marcaje3. Doesn't matter if it's a log table, the column needs to be indexed.

Tara Kizer
SQL Server MVP since 2007
http://weblogs.sqlteam.com/tarad/
Go to Top of Page

barnabeck
Posting Yak Master

236 Posts

Posted - 2015-03-12 : 18:34:48
Tara, ok, I choose the red pill in order to learn something new and leave the blue one that would just provide me with the results...

I did:

select * from sys.indexes
where object_id = (select object_id from sys.objects where name = 'dbo.Marcaje3')

to see if there isn't any index implemented. Nothing returned.
- This does most likely mean that there isn't any index.
- Nevertheless the table does have an unique key column... good! (I guess)
- this table is part of the industrial software that communicates with the terminals of the time clock. You can understand that I don't want to change anything that might alter the way everything is working right now. So I rather do not add any new column in that table. They will kill me!
- I looked up the commands for creating an index, but can't see how to add the index to an existing column.

CREATE INDEX index_name ON table_name (column_name)



Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2015-03-12 : 18:49:52
Since it's a vendor database, you'll need to get permission to add the index. I definitely was not suggesting adding another column, just an index on the column so that your query is more efficient.

Yes that would be the syntax, just swap out index_name, table_name and column_name for your values. index_name is whatever you want to name it. Naming suggestion: ix_TableName_ColumnName.

CREATE INDEX ix_marcaje3_USERID ON marcaje3 (USERID)

Tara Kizer
SQL Server MVP since 2007
http://weblogs.sqlteam.com/tarad/
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2015-03-12 : 18:53:17
Oh and your sysindexes query isn't correct. Remove "dbo." from it as that's not part of the object's name. It is the schema owner. So re-check it without dbo. to see if you have an index already. Or just use SSMS' object explorer to navigate it.

Tara Kizer
SQL Server MVP since 2007
http://weblogs.sqlteam.com/tarad/
Go to Top of Page

SwePeso
Patron Saint of Lost Yaks

30421 Posts

Posted - 2015-03-13 : 03:19:57
There is a spelling error...
with marcaje as

(SELECT *
from marcaje3
WHERE USERID = @USERID OR @USERID = '*'),

OrderTime as
(SELECT *, ROW_NUMBER() OVER (PARTITION BY USERID, DATEADD(day, DATEDIFF(day, 0, SCHEDULETIME), 0) ORDER BY SCHEDULETIME) AS rn
From marcajes)-- Should be only marcaje as in the previous cte, otherwise the table.

SELECT *
FROM OrderTime T1
left outer Join OrderTime T2 on DATEADD(day, DATEDIFF(day, 0, T1.SCHEDULETIME), 0) = DATEADD(day, DATEDIFF(day, 0, T2.SCHEDULETIME), 0) and
T1.USERID = T2.USERID and
T1.rn = T2.rn - 1



Microsoft SQL Server MVP, MCT, MCSE, MCSA, MCP, MCITP, MCTS, MCDBA
Go to Top of Page

barnabeck
Posting Yak Master

236 Posts

Posted - 2015-03-13 : 03:33:55
@SwePeso... you are right, but this is not the original query. I just ripped of everything to make clear the structure

@Tara... thank you for your last comment. I removed the dbo prefix and 3 indexes are returned. One clustered, unique one which is the primary key and two nonclustered ones...
This makes me feel stuck again... I hoped to fix it creating the index.
You also said "Instead clear the bad plan from cache and then rerun it" how would I do that?

Martin
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2015-03-13 : 13:25:50
Which version of SQL Server are you using?

For the two nonclustered indexes, do either of them lead off with USERID?

Tara Kizer
SQL Server MVP since 2007
http://weblogs.sqlteam.com/tarad/
Go to Top of Page

barnabeck
Posting Yak Master

236 Posts

Posted - 2015-03-14 : 17:58:02
On that server SQL 2005 is running

In fact one of the nonclustered indexes is related to USERID through another table (and a rather CPU consuming relationship - at least I guess so)

SELECT a.USERID

FROM Marcaje3
left outer join Usuario2_ConaccNis2 a on Marcaje3.nis = a.nis and
Marcaje3.SCHEDULETIME between a.fechaAct and
CASE WHEN DATEADD(day, DATEDIFF(day, 0, a.fechaAct), 0) = DATEADD(day, DATEDIFF(day, 0, a.fechaAnu), 0) and
a.estaAnulado = 0
THEN getdate()
ELSE a.fechaAnu
END


Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2015-03-16 : 12:26:11
quote:

In fact one of the nonclustered indexes is related to USERID through another table



What do you mean by that? An index doesn't reference another table.

Tara Kizer
SQL Server MVP since 2007
http://weblogs.sqlteam.com/tarad/
Go to Top of Page

barnabeck
Posting Yak Master

236 Posts

Posted - 2015-03-17 : 03:41:37
I just meant to answer your question if one of the nonclustered indexes of marcaje3 was related to USERID. USERID isn't a column of marcaje3.
Probably I don't understand the expression "to lead off with"
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2015-03-17 : 12:14:40
Your query indicates USERID is part of marcaje3: SELECT * from marcaje3 WHERE USERID...

Show us the CREATE TABLE statement for marcaje3 and also the CREATE INDEX statements for the 3 indexes that you found. You can do this through the SSMS GUI by right clicking on them and scripting them out.

Tara Kizer
SQL Server MVP since 2007
http://weblogs.sqlteam.com/tarad/
Go to Top of Page

barnabeck
Posting Yak Master

236 Posts

Posted - 2015-03-18 : 05:34:39
Tara, I had simplified the structure of that query leaving all the joins away in order to emphasize on the where clause and to make it more easy to understand.
It is 140 lines long but I will post it below...
FechaHora is what I was referring to as SCHEDULETIME, the Dummy-Condition is marked in red

1) CREATE TABLE ... marcaje3:
----------------------------
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[Marcaje3](
[idOrden] [int] IDENTITY(0,1) NOT NULL,
[idCli] [int] NOT NULL,
[idInst] [int] NOT NULL,
[nis] [bigint] NOT NULL,
[fechaHora] [datetime] NOT NULL,
[st] [int] NOT NULL,
[idCentro] [int] NOT NULL,
[idNodo] [int] NOT NULL,
[idTerm] [int] NOT NULL,
[idInci] [int] NOT NULL,
[esEntrada] [bit] NOT NULL,
[esSalida] [bit] NOT NULL,
[esManual] [bit] NOT NULL,
[estaModificado] [bit] NOT NULL,
[idOElab] [int] NOT NULL,
[idOp] [int] NOT NULL,
[cantidad] [int] NOT NULL,
[tipo] [int] NOT NULL,
[error] [int] NOT NULL,
[jornada] [datetime] NULL,
[otros] [text] NOT NULL DEFAULT (''),
[esEntradaCondicional] [bit] NULL DEFAULT ((0)),
[esSalidaCondicional] [bit] NULL DEFAULT ((0)),
[esAlertaLocal] [bit] NULL DEFAULT ((0)),
[zonaPanel] [int] NULL,
[naturaleza] [int] NULL,
[login] [varchar](64) NULL,
[aplicacion] [int] NULL,
[str1] [varchar](255) NULL,
[str2] [varchar](255) NULL,
[str3] [varchar](255) NULL,
[Especial] [int] NULL DEFAULT ((0)),
[esEntradaUsoEspecial2] [bit] NULL DEFAULT ((0)),
[esSalidaUsoEspecial2] [bit] NULL DEFAULT ((0)),
PRIMARY KEY CLUSTERED
(
[idOrden] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO


2a) CREATE INDEX Marcaje_Nis (Non-Unique, Non-Clustered)
--------------------------------------------------------
CREATE NONCLUSTERED INDEX [Marcaje_Nis] ON [dbo].[Marcaje3]
(
[idCli] ASC,
[idInst] ASC,
[nis] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO


2b) CREATE INDEX Marcaje3_NIS_fechaHora (Non-Unique, Non-Clustered)
-------------------------------------------------------------------
CREATE NONCLUSTERED INDEX [Marcaje3_NIS_fechaHora] ON [dbo].[Marcaje3]
(
[idCli] ASC,
[idInst] ASC,
[nis] ASC,
[fechaHora] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

2c) CREATE INDEX PK__Marcaje3__3CBF0154 (Clustered)
--------------------------------------------------
ALTER TABLE [dbo].[Marcaje3] ADD PRIMARY KEY CLUSTERED 
(
[idOrden] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO


3. Complete QUERY
-----------------
with marcajes as
(
SELECT Marcaje3.idOrden, Marcaje3.nis, Marcaje3.fechahora, Marcaje3.idInci, Usuario2_ConaccNis2.idUsr, idSeccion, Nombre + ' ' + Apellido1 as Nombre,
CASE WHEN Marcaje3.idInci <> 0
THEN (SELECT idSeccion
FROM Incidencia
WHERE Incidencia.idInci = Marcaje3.idInci)
ELSE idSeccion
END as Seccion
FROM Marcaje3
left outer join Usuario2_ConaccNis2 on Marcaje3.nis = Usuario2_ConaccNis2.nis and fechaHora between fechaAct and
CASE WHEN DATEADD(day, DATEDIFF(day, 0, fechaAct), 0) = DATEADD(day, DATEDIFF(day, 0, fechaAnu), 0) and estaAnulado = 0
THEN getdate()
ELSE fechaAnu END
left outer join Usuario2 on Usuario2_ConaccNis2.idusr = Usuario2.idUsr
WHERE Marcaje3.error = 0 and
(CASE WHEN len(@timeframe) = 10 then Convert(Varchar(20),Marcaje3.fechahora,104) ELSE @timeframe end) = @timeframe and
(CASE WHEN left(@timeframe,1) = 'm' THEN cast(right(@timeframe,2) as int) ELSE month(Marcaje3.fechaHora) END) = month(Marcaje3.fechaHora) and
(CASE WHEN left(@timeframe,1) = 'w' THEN dbo.fnISOWeek(Marcaje3.fechaHora) ELSE cast(right(@timeframe,2) as int) END) = cast(right(@timeframe,2) as int) and
(CASE WHEN len(@timeframe) <> 10 THEN cast(left(right(@timeframe,6),4) as int) ELSE year(Marcaje3.fechaHora) END) = year(Marcaje3.fechaHora) and
(Usuario2_ConaccNis2.idUsr = @Setnis or @Setnis = 1000000)
),
OrderTime as
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY idUsr, DATEADD(day, DATEDIFF(day, 0, fechaHora), 0) ORDER BY fechahora) AS rn
From marcajes
)

SELECT CASE WHEN @checkbox1='1' THEN NULL ELSE nis END as nis,
CASE WHEN @checkbox1='1' THEN NULL ELSE fechaHora END as fechaHora,
CASE WHEN @checkbox1='1' THEN NULL ELSE fechaFinal END as fechaFinal,
CASE WHEN @checkbox1='1' THEN NULL ELSE horario END as horario,
CASE WHEN @checkbox1='1' THEN NULL ELSE idInci END as idInci, idUsr, Nombre, SUM(Minutes) as Minutes,
SeccionDescrip, Seccion,
convert(varchar(15), sum(minutes) / 60 ) + ':' + REPLICATE('0', 2 - DATALENGTH(convert(varchar(2), (sum(minutes) % 60)))) + convert(varchar(2), (sum(minutes) % 60)) as minhoras

FROM (
SELECT *,
(SELECT SUM(
CASE WHEN horaFin < datepart(hh,fechaHora)*60+datepart(mi,fechaHora) OR horaIni > datepart(hh,fechaFinalCalc)*60 + datepart(mi,fechaFinalCalc) THEN 0 ELSE
CASE WHEN horaFin < datepart(hh,fechaFinalCalc)*60+datepart(mi,fechaFinalCalc) THEN horaFin ELSE datepart(hh,fechaFinalCalc)*60 + datepart(mi,fechaFinalCalc) END -
CASE WHEN horaIni > datepart(hh,fechaHora)*60+datepart(mi,fechaHora) THEN horaIni ELSE datepart(hh,fechaHora)*60 + datepart(mi,fechaHora) END
END)
FROM (SELECT COALESCE(Horario_Especial.horaIni, COALESCE(HorarioTeorico3.horaIni, HorarioTeorico2.horaIni)) as horaIni,
COALESCE(Horario_Especial.horaFin, COALESCE(HorarioTeorico3.horaFin, HorarioTeorico2.horaFin)) as horaFin,
tot.fechaHora,
tot.fechaFinalCalc
FROM Usuario2_Calendario
LEFT OUTER JOIN Horario_Especial on
((Horario_Especial.idUsr = tot.idUsr and Horario_Especial.aplica = 0) or
(Horario_Especial.idUsr = tot.idSeccion and Horario_Especial.aplica = 1)) and
(tot.fechaHora between Horario_Especial.FechaIni and Horario_Especial.FechaFin) and
((datepart(dw,tot.fechaHora) <> 6 and Horario_Especial.sabado = 0) or Horario_Especial.sabado = 1) and
((datepart(dw,tot.fechaHora) <> 7 and Horario_Especial.domingo = 0) or Horario_Especial.domingo = 1)
LEFT OUTER JOIN Diario2 on
Diario2.idUsr = tot.idUsr and
DATEADD(day, DATEDIFF(day, 0, tot.fechaHora), 0) = DATEADD(day, DATEDIFF(day, 0, Diario2.fecha), 0) and
Horario_Especial.horaIni IS NULL
LEFT OUTER JOIN HorarioTeorico as HorarioTeorico3 on
Diario2.iDHorario = HorarioTeorico3.idHorario and
Horario_Especial.horaIni IS NULL
LEFT OUTER JOIN dbo.Calendario_has_Semanario ON
Usuario2_Calendario.idCalendario = dbo.Calendario_has_Semanario.idCalendario AND
Calendario_has_Semanario.dia = DAY(tot.fechaHora) AND
Calendario_has_Semanario.mes = MONTH(tot.fechaHora) and
Horario_Especial.horaIni IS NULL and
HorarioTeorico3.horaIni IS NULL
LEFT OUTER JOIN dbo.Semanario_has_Horario ON
Calendario_has_Semanario.idSemanario = dbo.Semanario_has_Horario.idSemanario AND
DATEPART(dw, tot.fechaHora) - 1 = dbo.Semanario_has_Horario.diaSemana and
Horario_Especial.horaIni IS NULL and
HorarioTeorico3.horaIni IS NULL
LEFT OUTER JOIN dbo.HorarioTeorico AS HorarioTeorico2 ON
Semanario_has_Horario.idHorario = HorarioTeorico2.idHorario and
Horario_Especial.horaIni IS NULL and
HorarioTeorico3.horaIni IS NULL
WHERE dbo.Usuario2_Calendario.idUsr = tot.idUsr
)Cal) as Minutes,
(SELECT stuff((select ' ' + '<a href=Calendario_horario.aspx?fecha='+ Convert(Varchar(20), tot.FechaHora,104) +'&idUsr='+ CAST(tot.idUsr as nvarchar) +' target=#34;main#34; style=#34;text-decoration: none#34; >' + COALESCE(Horario_Especial.Marker,'') + CAST(COALESCE(Horario_Especial.horaIni,COALESCE(HorarioTeorico3.horaIni, HorarioTeorico2.horaIni)) / 60 AS nvarchar)
+ ':' + LEFT(CAST(COALESCE(Horario_Especial.horaIni, COALESCE(HorarioTeorico3.horaIni, HorarioTeorico2.horaIni)) - 60 * (COALESCE(Horario_Especial.horaIni, COALESCE(HorarioTeorico3.horaIni, HorarioTeorico2.horaIni)) / 60) AS nvarchar) + '0', 2) + ' - ' + CAST(COALESCE(Horario_Especial.horaFin, COALESCE(HorarioTeorico3.horaFin, HorarioTeorico2.horaFin)) / 60 AS nvarchar)
+ ':' + LEFT(CAST(COALESCE(Horario_Especial.horaFin, COALESCE(HorarioTeorico3.horaFin, HorarioTeorico2.horaFin)) - 60 * (COALESCE(Horario_Especial.horaFin, COALESCE(HorarioTeorico3.horaFin, HorarioTeorico2.horaFin)) / 60) AS nvarchar) + '0', 2) +'</a> '

FROM Usuario2_Calendario
LEFT OUTER JOIN Horario_Especial on
((Horario_Especial.idUsr = tot.idUsr and Horario_Especial.aplica = 0) or
(Horario_Especial.idUsr = tot.idSeccion and Horario_Especial.aplica = 1)) and
(fechaHora between Horario_Especial.FechaIni and Horario_Especial.FechaFin) and
((datepart(dw,FechaHora) <> 6 and Horario_Especial.sabado = 0) or Horario_Especial.sabado = 1) and
((datepart(dw,FechaHora) <> 7 and Horario_Especial.domingo = 0) or Horario_Especial.domingo = 1)
LEFT OUTER JOIN Diario2 on
Diario2.idUsr = tot.idUsr and
Convert(Varchar(20),FechaHora,104) = Convert(Varchar(20),Diario2.fecha,104) and
Horario_Especial.horaIni IS NULL
LEFT OUTER JOIN HorarioTeorico as HorarioTeorico3 on
Diario2.idHorario = HorarioTeorico3.idHorario and
Horario_Especial.horaIni IS NULL
LEFT OUTER JOIN dbo.Calendario_has_Semanario ON
Usuario2_Calendario.idCalendario = dbo.Calendario_has_Semanario.idCalendario AND
Calendario_has_Semanario.dia = DAY(fechaHora) AND
Calendario_has_Semanario.mes = MONTH(fechaHora) and
Horario_Especial.horaIni IS NULL and
HorarioTeorico3.horaIni IS NULL
LEFT OUTER JOIN dbo.Semanario_has_Horario ON
Calendario_has_Semanario.idSemanario = dbo.Semanario_has_Horario.idSemanario AND
DATEPART(dw, fechaHora) - 1 = dbo.Semanario_has_Horario.diaSemana and
Horario_Especial.horaIni IS NULL and
HorarioTeorico3.horaIni IS NULL
LEFT OUTER JOIN dbo.HorarioTeorico AS HorarioTeorico2 ON
Semanario_has_Horario.idHorario = HorarioTeorico2.idHorario and
Horario_Especial.horaIni IS NULL and
HorarioTeorico3.horaIni IS NULL

WHERE dbo.Usuario2_Calendario.idUsr = tot.idUsr
ORDER BY COALESCE(HorarioTeorico2.horaIni,Horario_Especial.horaIni)
for xml path(''),type).value('.', 'nvarchar(max)'), 1, 1, '')) as horario

FROM (
SELECT T1.*, T2.fechaHora as fechaFinal,
CASE WHEN DATEADD(day, DATEDIFF(day, 0, T1.fechaHora), 0) = DATEADD(day, DATEDIFF(day, 0, COALESCE(T2.fechaHora, getdate())), 0) and T1.idInci <> 2 and T1.idInci <> 9
THEN COALESCE (T2.fechaHora, CASE WHEN T1.rn = 1 or T1.idInci <> 0 THEN getdate() ELSE T1.fechaHora END)
ELSE T1.fechaHora
END as fechaFinalCalc,
COALESCE ((SELECT descrip from Seccion WHERE IDSeccion = T1.Seccion), (SELECT descrip from Incidencia WHERE Incidencia.idInci = T1.idInci)) as SeccionDescrip
FROM OrderTime T1
left outer Join OrderTime T2 on DATEADD(day, DATEDIFF(day, 0, T1.fechaHora), 0) = DATEADD(day, DATEDIFF(day, 0, T2.fechaHora), 0) and
T1.idUsr = T2.IdUsr and
T1.rn = T2.rn - 1
WHERE (T2.fechaHora IS NOT NULL OR (T2.fechahora IS NULL and T1.idInci <> 0) OR (T2.fechahora IS NULL and T1.idInci = 0 and T1.rn = 1))
)Tot
)SUM
GROUP BY CASE WHEN @checkbox1='1' THEN NULL ELSE nis END,
CASE WHEN @checkbox1='1' THEN NULL ELSE fechaHora END,
CASE WHEN @checkbox1='1' THEN NULL ELSE fechaFinal END,
CASE WHEN @checkbox1='1' THEN NULL ELSE idInci END,
CASE WHEN @checkbox1='1' THEN NULL ELSE horario END,
Seccion,
SeccionDescrip,
idUsr, Nombre
ORDER by fechaHora desc, Minutes desc

Martin
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2015-03-18 : 12:32:13
Then check Usuario2_ConaccNis2 to see if any indexes have the first column as idUsr.

Tara Kizer
SQL Server MVP since 2007
http://weblogs.sqlteam.com/tarad/
Go to Top of Page

barnabeck
Posting Yak Master

236 Posts

Posted - 2015-03-19 : 06:33:54
I did:
USE CONACC_BD;
GO
EXEC sp_helpindex 'Usuario2_ConaccNis2'
GO
and it returned
index_name                         index_description                                      index_keys
------------------------------ ------------------------------------------------- -----------------------------------
PK__Usuario2_ConaccN__0DCF0841 clustered, unique, primary key located on PRIMARY idCli, idInst, idUsr, nis, fechaAct
hope that this answers the question, though I don't know if it matters that idUsr is the third and not first column, as you wanted it to be...
That table has these 5 primary keys (didn't know this was possible)

Thank you,
Martin
Go to Top of Page

Kristen
Test

22859 Posts

Posted - 2015-03-19 : 08:47:00
I've only looked at it briefly, so may not be addressing the central issue, but:

left outer join Usuario2 on Usuario2_ConaccNis2.idusr = Usuario2.idUsr

I expect that idUsr needs to be the first key field in the index for the index to be used

and

WHERE Marcaje3.error = 0 and
(CASE WHEN len(@timeframe) = 10 then Convert(Varchar(20),Marcaje3.fechahora,104) ELSE @timeframe end) = @timeframe and
(CASE WHEN left(@timeframe,1) = 'm' THEN cast(right(@timeframe,2) as int) ELSE month(Marcaje3.fechaHora) END) = month(Marcaje3.fechaHora) and
(CASE WHEN left(@timeframe,1) = 'w' THEN dbo.fnISOWeek(Marcaje3.fechaHora) ELSE cast(right(@timeframe,2) as int) END) = cast(right(@timeframe,2) as int) and
(CASE WHEN len(@timeframe) <> 10 THEN cast(left(right(@timeframe,6),4) as int) ELSE year(Marcaje3.fechaHora) END) = year(Marcaje3.fechaHora) and
(Usuario2_ConaccNis2.idUsr = @Setnis or @Setnis = 1000000)

is so complex (in particular the use of Functions to manipulate the data and CASE before comparing it) that I think SQL will use a scan regardless ...

Personally I would change the tables to contain the data in away that this type of query can be satisfied with a more simple WHERE clause.
It doesn't help having @timeframe as a composite parameter either - split that up, before this query, so that you can replace:

CASE WHEN left(@timeframe,1) = 'm'

with

CASE WHEN @timeframe_TYPE = 'm'

and

cast(right(@timeframe,2) as int)

with

@int_timeframe_VALUE

or somesuch. I also think that would make the code more readable, and thus less likely that bugs are introduced during future maintenance etc.
Go to Top of Page

barnabeck
Posting Yak Master

236 Posts

Posted - 2015-03-19 : 11:18:29
Interesting point, Kristen. In order to check if the time-related portion in the WHERE clause has an impact on the execution time and on the need to put the dummy-condition to reduce it, I simplified the query looking just one month:
WHERE Marcaje3.error = 0 and Month(Marcaje3.fechaHora)= 1 and year(Marcaje3.fechaHora) = 2015 and
(Usuario2_ConaccNis2.idUsr = @Setnis)
WHERE Marcaje3.error = 0 and Month(Marcaje3.fechaHora)= 1 and year(Marcaje3.fechaHora) = 2015 and
(Usuario2_ConaccNis2.idUsr = @Setnis or @Setnis = 1000000)
The first Query took 14 seconds, while the second took 2 seconds. Now you draw the conclusions... I still think -as Tara convinced me- that it is mainly an index issue.

So -as I'm learning from your answers- the order of the key fields matters and one index can be related to a series of key fields...
So, can I imagine indexing as something like a sort-by-clause, with several fields. The result would be like an order number, attached to each row, which is the index? Does this idea comes close?

Unfortunately, as it has been said earlier, this is a vendors database and I will contact these guys in order to make them change anything regarding the indexes only if I'm really sure about it's impact on my query. They might not be too happy about what I'm doing with "their" databases as they might have a software-solution to exactly what I am doing.

... and I have to admit that I'm still kind of proud of the complex WHERE clause, as I couldn't think of anything more compact to search for records in a timeframe, that is defined by one single input parameter which can specify day, week or month. But I guess you are right that compactness doesn't convert into execution time.

Martin
Go to Top of Page

Kristen
Test

22859 Posts

Posted - 2015-03-19 : 11:49:06
Those MONTH() and YEAR() functions may still be hurting you though ... worth comparing your tes timings with this (assuming I have understood that Marcaje3.fechaHora is a Date field) which avoids using any functions :

WHERE Marcaje3.error = 0 and Marcaje3.fechaHora >= '20150101' AND Marcaje3.fechaHora < '20150201' AND
(Usuario2_ConaccNis2.idUsr = @Setnis)

and then comparing that with your additional test for or @Setnis = 1000000

The order of key fields only matters (in general) if you are doing Wildcard or Range queries.

SELECT ... FROM ... WHERE MyColumn LIKE 'FOOBAR%'
or
SELECT ... FROM ... WHERE MyColumn > 10000 AND MyColumn < 20000

can only make use of an index if MyColumn is the first key field

SELECT ... FROM ... WHERE MyKey1 = 'XXX' AND MyColumn LIKE 'FOOBAR%'
or
SELECT ... FROM ... WHERE MyKey1 = 'XXX' AND MyColumn > 10000 AND MyColumn < 20000

in this example MyColumn can be the second key field because the first key field [MyKey1] has an explicit value, so the index wildcard is XXX,FOORBAR% or the range is XXX,10000 to XXX,20000 which is a specific contiguous section of the index.

But in the first example if MyColumn was the second key field then the search would be for *ANYTHING,FOORBAR% or ANYTHING,10000 to ANYTHING,20000 and the optimiser usually decides it will be quicker to SCAN the table rather than SCAN the index and then lookup each individual match in the table (which requires two disk reads).

In practice it isn't always as simple as that ... but its a good First Principle.

quote:
... and I have to admit that I'm still kind of proud of the complex WHERE clause, as I couldn't think of anything more compact to search for records in a timeframe, that is defined by one single input parameter which can specify day, week or month


Its elegant, but very inefficient.

In such situations we often repeat the whole query, multiple times, with different WHERE clauses and the code uses whichever of the queries is best suited to the parameters that were actually provided (by the user presumably), this is especially true where the user has a Criteria Form which allows them to search by all sorts of different criteria - Invoices by Customer, Invoices by Date, Invoices containing a specific Product ... they all require radically different optimisations to be efficient. The User still likes to have a "Do Anything" query form :) just the code-behind has to be more complex to remain efficient.

If the database table is small it doesn't matter ... big database, lot of people using it, then inefficient queries can bring the server to its knees.
Go to Top of Page
    Next Page

- Advertisement -