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.
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 = 39162131with 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 = @USERIDTo 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 upAny 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 KizerSQL Server MVP since 2007http://weblogs.sqlteam.com/tarad/ |
|
|
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 |
|
|
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 KizerSQL Server MVP since 2007http://weblogs.sqlteam.com/tarad/ |
|
|
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 KizerSQL Server MVP since 2007http://weblogs.sqlteam.com/tarad/ |
|
|
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.indexeswhere 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) |
|
|
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 KizerSQL Server MVP since 2007http://weblogs.sqlteam.com/tarad/ |
|
|
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 KizerSQL Server MVP since 2007http://weblogs.sqlteam.com/tarad/ |
|
|
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 |
|
|
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 |
|
|
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 KizerSQL Server MVP since 2007http://weblogs.sqlteam.com/tarad/ |
|
|
barnabeck
Posting Yak Master
236 Posts |
Posted - 2015-03-14 : 17:58:02
|
On that server SQL 2005 is runningIn 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.USERIDFROM 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 |
|
|
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 KizerSQL Server MVP since 2007http://weblogs.sqlteam.com/tarad/ |
|
|
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" |
|
|
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 KizerSQL Server MVP since 2007http://weblogs.sqlteam.com/tarad/ |
|
|
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 red1) CREATE TABLE ... marcaje3:----------------------------SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOSET ANSI_PADDING ONGOCREATE 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]GOSET ANSI_PADDING OFFGO 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 |
|
|
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 KizerSQL Server MVP since 2007http://weblogs.sqlteam.com/tarad/ |
|
|
barnabeck
Posting Yak Master
236 Posts |
Posted - 2015-03-19 : 06:33:54
|
I did:USE CONACC_BD;GOEXEC sp_helpindex 'Usuario2_ConaccNis2'GO and it returnedindex_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 |
|
|
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 usedand 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' withCASE WHEN @timeframe_TYPE = 'm' andcast(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. |
|
|
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 |
|
|
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 = 1000000The order of key fields only matters (in general) if you are doing Wildcard or Range queries.SELECT ... FROM ... WHERE MyColumn LIKE 'FOOBAR%'orSELECT ... FROM ... WHERE MyColumn > 10000 AND MyColumn < 20000 can only make use of an index if MyColumn is the first key fieldSELECT ... FROM ... WHERE MyKey1 = 'XXX' AND MyColumn LIKE 'FOOBAR%'orSELECT ... 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. |
|
|
Next Page
|
|
|
|
|