DECLARE @t1 TABLE (T1_key int, T1_Data char(1), T1_ValidUntil datetime)
DECLARE @t2 TABLE (T2_key int, T2_Data char(1), T2_ValidUntil datetime)
INSERT @t1 VALUES (1, 'A', '2000-01-01')
INSERT @t1 VALUES (1, 'B', '2000-06-30')
INSERT @t1 VALUES (2, 'C', '2005-05-31')
INSERT @t1 VALUES (3, 'D', '2004-12-31')
INSERT @t1 VALUES (3, 'E', '2007-04-30')
INSERT @t1 VALUES (3, 'F', '2008-01-31')
INSERT @t2 VALUES (1, 'R', '2002-03-31')
INSERT @t2 VALUES (2, 'S', '2001-06-30')
INSERT @t2 VALUES (2, 'T', '2003-02-28')
INSERT @t2 VALUES (2, 'U', '2005-05-31')
INSERT @t2 VALUES (3, 'V', '2006-09-30')
INSERT @t2 VALUES (3, 'W', '2007-06-30')
SELECT
T1.*, T2x.*
FROM
@t1 T1
CROSS APPLY
(SELECT TOP 1*
FROM @t2
WHERE T1_key = T2_key AND T2_ValidUntil >= T1_ValidUntil
ORDER BY T2_ValidUntil
) T2x
UNION
SELECT
T1x.*, T2.*
FROM
@t2 T2
CROSS APPLY
(SELECT TOP 1*
FROM @t1
WHERE T1_key = T2_key AND T1_ValidUntil >= T2_ValidUntil
ORDER BY T1_ValidUntil
) T1x
Wildcard matching strings with good performance will always be tricky, but here's a best-effort attempt.
First off, your indexing can be improved. The index on #PARAMETERS only includes the actual ParameterName
column, which is fine if you need a list of parameters, but whenever you need any other column, the index will be useless to SQL Server, and it'll revert to scanning the entire table instead. I would propose fixing this by changing the index to a clustered index:
CREATE UNIQUE CLUSTERED INDEX [IX_PARAMETERS]
ON #PARAMETERS (ParameterName, [Datetime])
WITH (DROP_EXISTING=ON); -- Remove this row if the index doesn't already exist.
Clustered indexes include every column in the table, which automatically makes a clustered index a covering indexes.
From here, I've updated your query to do three things:
- Collect each unique parameter in #PARAMETERS.
- For each unique parameter, find matching formulas in #FORMULAS. This is the expensive part, and no index will really improve this search because the search condition (LIKE) is not sargable. This means that, index or not, SQL Server will go through each and every row in the table (a so-called scan), for every parameter.
- Finally, join #PARAMETERS to get all the dates and values. This is a relatively cheap and efficient operation. This is where the modified index on #PARAMETERS helps.
Here's the resulting query:
SELECT A.[Datetime],
p.[ParameterName],
B.[Description],
A.[Value]
FROM (SELECT DISTINCT ParameterName FROM #PARAMETERS) p
CROSS APPLY (
SELECT *
FROM #FORMULAS B WITH (FORCESCAN)
WHERE B.DataSourceAddress LIKE N'%'+p.ParameterName+N'%') AS B
INNER JOIN #PARAMETERS AS A ON
p.ParameterName=A.ParameterName;
I added WITH (FORCESCAN)
to eliminate key lookups. You can try the query with or without this hint.
If I had a lot more time to work on this and it was a super-critical query, I would probably (a) remodel the tables and/or (b) consider storing the data in #FORMULAS as XML data and add XML indexes, which would eliminate the LIKE match.
Footnote: Text matching like you do here (LIKE
and CHARINDEX
are pretty much the same here) will include formulas with the parameters "parameter10" and "parameter11" even when you're just searching for "parameter1".
Best Answer
As far as you have not provided an expected result I've used this:
dbfiddle here
Where 1 means attendance and 0 not attendance.
I've used next script to dynamically generate the pivot table.
First CTE generates all possible dates using tbl_month, you should set some filter (date between MinDate and MaxDate) or it will use the whole calendar table.
Data source
Using a LEFT JOIN with all possible dates, I've set a mark (0/1) depending on employees attendance.
Pivot columns
Keep in mind I've used
CONVERT(varchar(20),cal_dates,103)
, as a result you get a date format as 'DD/MM/YYYY'. If you want to change it, remember to change both, pivot columns generator and data source select.No attendance only
If you want to fetch 'Non attendance' only, you can filter data source on this way:
Change dates by employess
Once you have defined the dynamic query, you can easily change final output, and interchange dates by employees.
This is the result:
dbfiddle here