Query improvement in where clause - sql

I have the following query:
DECLARE #MyTable TABLE
(
[ID] INT ,
[Col1] INT ,
[Col2] INT
)
INSERT INTO #MyTable
SELECT 1 ,
2 ,
1
UNION
SELECT 1 ,
2 ,
3
UNION
SELECT 2 ,
2 ,
3
UNION
SELECT 2 ,
2 ,
3
UNION
SELECT 3 ,
2 ,
3
UNION
SELECT 3 ,
2 ,
1
DECLARE #ID INT
SET #ID = 1
SELECT *
FROM #MyTable
WHERE ( Col1 = ( CASE WHEN [ID] = #ID THEN 2
END )
OR [Col2] = ( CASE WHEN [ID] != #ID THEN 1
END )
)
WHEN [ID] = #ID I want to match Col1 with constant value equals to 2 and when [ID] != #ID I want to match Col2 with constant value equals to 1. Can the above query be improve so that [ID] equality check can be done only once in the above query, something like this:
SELECT *
FROM #MyTable
WHERE
if([ID] = #ID)
Col1=2
ELSE
[Col2]=1

Is this the logic that you want?
where id = #id and col1 = 2 or
id <> #id and col2 = 1
I don't know why you are concerned about the performance of such a clause. You can do what you want with a case statement:
where 1 = (case when id = #id
then (case when col1 = 2 then 1 end)
else col2 = 1
end)
But this is a needless "optimization". It is not even clear that the nested case statements would be any faster than the first version. Such simple operations are really, really fast on modern computers. And, what slows databases down is the processing of large volumes of data (in general), not such simple operations.

Perhaps just:
Select *
From #MyTable
Where ((id = #id and col1 = 2) or (id <> #id and col2 = 1))

Related

Optimizing the Sql Server 2019 Query [ About Categories with Id and ParentId Relation using single column in table]

How can i optimize the Sql Server Query
Table name is: Pro_itemmaster
Column name is: itm_Code
Test Data = Download Link
My Query take 17 seconds to complete
Query
; WITH CatItem AS(
SELECT
PIM.itm_Code AS Id
,CASE WHEN LEN(PIM.itm_Code) = 2 THEN NULL ELSE LEFT(PIM.itm_Code, LEN(PIM.itm_Code) - CHARINDEX('-',REVERSE(PIM.itm_Code))) END AS ParentId
,1 AS [Depth]
FROM
Pro_itemmaster AS PIM
WHERE
LEN(PIM.itm_Code) = 2
UNION ALL
SELECT
PIM.itm_Code AS Id
,CASE WHEN LEN(PIM.itm_Code) = 2 THEN NULL ELSE LEFT(PIM.itm_Code, LEN(PIM.itm_Code) - CHARINDEX('-',REVERSE(PIM.itm_Code))) END AS ParentId
,[CatItem].[Depth] + 1 AS [Depth]
FROM
[Pro_itemmaster] AS [PIM]
JOIN
[CatItem]
ON
CASE WHEN LEN(PIM.itm_Code) = 2 THEN NULL ELSE LEFT(PIM.itm_Code, LEN(PIM.itm_Code) - CHARINDEX('-',REVERSE(PIM.itm_Code))) END = CatItem.Id
)
SELECT * FROM CatItem
Query Execution Plan
I am able to reduce the time to 3 seconds by introducing a table variable.
The issue here is that I cannot modify the main table and add ParentId column so needed the work around.
DECLARE #DataTable TABLE (Id VARCHAR(60) PRIMARY KEY NOT NULL, ParentId VARCHAR(60))
INSERT INTO #DataTable
SELECT itm_Code
, CASE WHEN LEN(itm_Code) = 2 THEN NULL ELSE LEFT(itm_Code, LEN(itm_Code) - CHARINDEX('-',REVERSE(itm_Code))) END
FROM Pro_itemmaster
; WITH CatItem AS(
SELECT
PIM.Id
,PIM.ParentId
,1 AS [Depth]
FROM
#DataTable AS PIM
WHERE
LEN(PIM.Id) = 2
UNION ALL
SELECT
PIM.Id
,PIM.ParentId
,[CatItem].[Depth] + 1 AS [Depth]
FROM
#DataTable AS [PIM]
JOIN
[CatItem]
ON
PIM.ParentId = CatItem.Id
)
SELECT *
FROM CatItem
ORDER BY Id

A SQL query to count how many attributes a record has in common with another record?

I am trying to write a SQL query to take the count of columns with equal value in my schema for each row by comparison to a single record.
Example:
record1: 1, 0, 1, 0, 0
record2: 0, 0, 0, 0, 1
record3: 0, 0, 1, 0, 0
record1 has 2 attributes in common with record2, go through the entire table and order by number of attributes each record has in common with record1
Is there a way to write a SQL statement that will do this? I have only found ways to compare each row and specify which attributes must be of equal value.
You can do:
select t.*,
((case when t.col1 = t1.col1 then 1 else 0 end) +
(case when t.col2 = t1.col2 then 1 else 0 end) +
(case when t.col3 = t1.col3 then 1 else 0 end) +
. . .
) as num_in_common
from t cross join
t t1
where t1.id = 1; -- or however you define "record1"
order by num_in_common desc;
Here's a nice routine you can use in SQL Server that will do this if you'd like. Replace #temp with your table name:
declare #holding table (id int, col1 int, col2 int, col3 int, col4 int, col5 int, num_in_common int)
declare #iterator int = 1
declare #row1col1 int, #row1col2 int, #row1col3 int, #row1col4 int ,#row1col5 int
while #iterator<=(select max(id) from #temp)
begin
if #iterator=1
select #row1col1=col1, #row1col2=col2, #row1col3=col3, #row1col4=col4 ,#row1col5=col5
from #temp where id=#iterator
else
insert #holding
select *, case when col1-#row1col1 = 0 then 1 else 0 end +
case when col2-#row1col2 = 0 then 1 else 0 end +
case when col3-#row1col3 = 0 then 1 else 0 end +
case when col4-#row1col4 = 0 then 1 else 0 end +
case when col5-#row1col5 = 0 then 1 else 0 end
from #temp where id=#iterator
set #iterator=#iterator+1
end
select * from #holding

"Error" value on name and description creating strange functionality

I got a raw data file with its content looking like this:
MSN_Check,Text,25,MSN check
0,Text,1,(Result)
HWIMPL,Text,10,HWIMPL version reading
007F,Text,6,(Measure)
1,Text,1,(Result)
VHW,Text,10,FMT hardware version
494131383346,Text,10,(Measure)
0,Text,1,(Result)
TOTAL_VER,Text,25,Total version reading
313031303130,Text,6,(Measure)
1,Text,1,(Result)
CAL_MCU,Text,25,Total version reading
05,Text,6,(Measure)
Error,Text,25,Error
9.8499985089315E-07,Numeric,Float 3.3,(Measure)
CAL_EEPROM,Text,25,Total version reading
05,Numeric,Float 3.3,(Measure)
1,Text,1,(Result)
And I needed to extract and store in variables the name, example MSN_Check ,the description, example MSN check its result for example 0 and its measure , for example 007F but in some places I have results only or measures only so just spliting them wouldn't have helped.So my idea was:
First of all I created a template table named dbo.template that looks like this:
Name TestDescription Measure Result ID
----------------------------------------------
MSN_Check MSN check 0 1 1
HWIMPL HWIMPL version reading 1 1 2
VHW FMT hardware version 1 1 3
TOTAL_VER Total version reading 1 1 4
CAL_MCU Total version reading 1 0 5
Error Error 1 0 6
CAL_EEPROM Total version reading 1 1 7
In this table we have the name,description,if_measure(meaning 1 if we have a measure or 0 if we dont) and the if_result.And I made a query looking like this:
DECLARE #crlf AS CHAR(2) = CHAR(13) + CHAR(10)
declare #testname varchar(max),#testDescription varchar(max), #if_measure char(1), #if_result char(1), #row int = '1', #id int
set #LogEntry = (SELECT REPLACE(#LogEntry,#crlf,','))
declare #name varchar(max),#description varchar(MAX), #measure varchar(20), #result char(1)
declare #Output table(OutTestName varchar(max),OUTTestDescription varchar(max), OutMeasure varchar(50), OutResult varchar(50))
declare #maximum int = (select MAX(ID) from dbo.template_FMT)
declare #LogEntry1 as nvarchar(max)
declare #LogEntry2 as nvarchar(max)
while #row <= #maximum
BEGIN
set #name = null
set #description = null
set #measure = null
set #result = null
set #testname = (select Name from dbo.template_FMT where ID = #row)
set #testDescription = (select TestDescription from dbo.template_FMT where ID = #row)
set #if_measure = (select Measure from dbo.template_FMT where ID = #row)
set #if_result = (select Result from dbo.template_FMT where ID = #row)
set #id = (select ID from dbo.Split(#LogEntry, ',') where Data = #testname)
SELECT #LogEntry1 = Name FROM dbo.template_FMT where id = #row
set #name = #LogEntry1
SELECT #LogEntry2 = TestDescription FROM dbo.template_FMT where id = #row
set #description = #LogEntry2
if #if_measure > 0 and #if_result > 0
begin
set #measure = (select Data from dbo.Split(#LogEntry, ',') where ID = #id+4)
set #result = (select Data from dbo.Split(#LogEntry, ',') where ID = #id+8)
insert into #Output (OutTestName, OUTTestDescription, OutMeasure, OutResult) Values(#name,#description, #measure, #result)
end
if #if_measure > 0 and #if_result = 0
begin
set #measure = (select Data from dbo.Split(#LogEntry, ',') where ID = #id+4)
set #result = null
insert into #Output (OutTestName, OUTTestDescription, OutMeasure, OutResult) Values(#name,#description, #measure, #result)
end
if #if_measure = 0 and #if_result > 0
begin
set #measure = null
set #result = (select Data from dbo.Split(#LogEntry, ',') where ID = #id+4)
insert into #Output (OutTestName, OUTTestDescription, OutMeasure, OutResult) Values(#name,#description, #measure, #result)
end
set #row = #row + 1
END
select * from #Output
And it worked! but the only problem I have is where I have the row with the name Error with the description Error,it would return the last remembered value so instead of having
CAL_MCU Total version reading 05 NULL
Error Error 9.8499985089315E-07 NULL
CAL_EEPROM Total version reading 05 1
I get:
CAL_MCU Total version reading 05 NULL
Error Error 05 NULL
CAL_EEPROM Total version reading 05 1
And I would like to store the Error cant find Result with ID into variables if any of you have any suggestions :)
P.S. I think it has something to do with the fact that the name and description have the same name (Error)
I believe that your problem can be solved without the need for while loops and string splitting functions. I recommend using the OPENROWSET function to read your raw data file as a standard table. You can then use standard T-SQL query to format the result into the desired output.
The first step is to ensure that ad-hoc queries is enable on your server this can be accomplished by executing the following command.
sp_configure 'show advanced options', 1;
RECONFIGURE;
GO
sp_configure 'Ad Hoc Distributed Queries', 1;
RECONFIGURE;
The next step is to define a format file for your text file. This will help SQL Server understand the text file structure when loading the raw data. Based on the supplied sample data your format file should look as follow:
10.0
4
1 SQLCHAR 0 100 "," 1 Col1 SQL_Latin1_General_CP1_CI_AS
2 SQLCHAR 0 100 "," 2 Col2 SQL_Latin1_General_CP1_CI_AS
3 SQLCHAR 0 100 "," 3 Col3 SQL_Latin1_General_CP1_CI_AS
4 SQLCHAR 0 100 "\r\n" 4 Col4 SQL_Latin1_General_CP1_CI_AS
I have uploaded the format file and example raw data file I have used to test the example at the following links:
http://www.filedropper.com/format
http://www.filedropper.com/rawdatafile
The final step is to run the OPENROWSET query to load the file data and transform the data to the desired output. If you are using SQL Server 2008 r2 then the following query should work:
-- 2008 R2 Version
WITH CTE_VariableRawData
AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 0)) AS ID
,[RawData].Col1 AS [VariableOrMeasure]
,(
CASE [RawData].Col4
WHEN '(Result)' THEN 0
WHEN '(Measure)' THEN 0
ELSE 1
END
) AS IsVariable
,(
CASE [RawData].Col4
WHEN '(Result)' THEN 1
ELSE 0
END
) AS IsResult
,(
CASE [RawData].Col4
WHEN '(Measure)' THEN 1
ELSE 0
END
) AS IsMeasure
,[RawData].Col4 AS [Description]
FROM OPENROWSET(BULK N'C:\temp\raw_data_file.txt', FORMATFILE = 'c:\temp\format.txt') AS [RawData]
)
,
CTE_RawDataByVariableID
AS
(
SELECT ID
,(
SELECT SUM([IsVariable])
FROM CTE_VariableRawData RunningTotal
WHERE RunningTotal.ID <= CTE_VariableRawData.ID
) AS VariableID
,[VariableOrMeasure]
,[IsVariable]
,[IsResult]
,[IsMeasure]
,[Description]
FROM CTE_VariableRawData
)
SELECT VariableID
,MAX(
CASE [IsVariable]
WHEN 1 THEN [VariableOrMeasure]
ELSE NULL
END
) AS [Variable]
,MAX(
CASE [IsVariable]
WHEN 1 THEN [Description]
ELSE NULL
END
) AS [Description]
,MAX(
CASE [IsMeasure]
WHEN 1 THEN [VariableOrMeasure]
ELSE NULL
END
) AS [Measure]
,MAX(
CASE [IsResult]
WHEN 1 THEN [VariableOrMeasure]
ELSE NULL
END
) AS [Result]
FROM CTE_RawDataByVariableID
GROUP BY VariableID
ORDER BY VariableID
If you are using SQL Server 2012 or later then the following query will be a bit more optimal:
WITH CTE_VariableRawData
AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 0)) AS ID
,[RawData].Col1 AS [VariableOrMeasure]
,(
CASE [RawData].Col4
WHEN '(Result)' THEN 0
WHEN '(Measure)' THEN 0
ELSE 1
END
) AS IsVariable
,(
CASE [RawData].Col4
WHEN '(Result)' THEN 1
ELSE 0
END
) AS IsResult
,(
CASE [RawData].Col4
WHEN '(Measure)' THEN 1
ELSE 0
END
) AS IsMeasure
,[RawData].Col4 AS [Description]
FROM OPENROWSET(BULK N'C:\temp\raw_data_file.txt', FORMATFILE = 'c:\temp\format.txt') AS [RawData]
)
,
CTE_RawDataByVariableID
AS
(
SELECT ID
,SUM([IsVariable]) OVER (ORDER BY ID) AS VariableID
,[VariableOrMeasure]
,[IsVariable]
,[IsResult]
,[IsMeasure]
,[Description]
FROM CTE_VariableRawData
)
SELECT VariableID
,MAX(
CASE [IsVariable]
WHEN 1 THEN [VariableOrMeasure]
ELSE NULL
END
) AS [Variable]
,MAX(
CASE [IsVariable]
WHEN 1 THEN [Description]
ELSE NULL
END
) AS [Description]
,MAX(
CASE [IsMeasure]
WHEN 1 THEN [VariableOrMeasure]
ELSE NULL
END
) AS [Measure]
,MAX(
CASE [IsResult]
WHEN 1 THEN [VariableOrMeasure]
ELSE NULL
END
) AS [Result]
FROM CTE_RawDataByVariableID
GROUP BY VariableID
ORDER BY VariableID;
Note that in both queries you will have to change the location of your raw data file and format file to the desired location within the OPENROWSET(BULK N'C:\temp\raw_data_file.txt', FORMATFILE = 'c:\temp\format.txt') call.

How to get a non matching record in single row

Table1
ID
001
002
001
001
001
...
I want to check the id from table1 where id should be even. If id is different then i need to return 2 else 1
How to write a query for this?
For IDs
SELECT (CASE WHEN [ID]%2 = 1 THEN 1 ELSE 2 END)
FROM [table]
For ID COUNT :
SELECT (CASE WHEN COUNT([ID])%2 = 1 THEN 1 ELSE 2 END)
FROM [table]
GROUP BY [ID]
Please check this.
declare #t table (id varchar(50))
insert into #t values('001'),('001'),('002'),('002'),('001'),('002'),('002')
SELECT
CASE WHEN cast( [ID] as int) %2 = 1 THEN 1 ELSE 2 END oddOrEven
FROM #t
--for counting
;with cte as
(
SELECT [ID]%2 value,
CASE cast( [ID] as int) %2 when 1 THEN count(1) else count(2) END oddCount
FROM #t
group by id
)
select * from cte
If I understand the question correctly, a CASE statement is not necessary here. I'm assuming you want to return 2 when ID is even, and 1 when ID is odd? As long as there aren't any non-digit characters in the values of the ID column, you can do the following:
SELECT [ID], 2 - CAST([ID] AS int) % 2
FROM Table1
If you want to return 2 when ID is odd, and 1 when it is even (sorry, that wasn't clear from the question), then you can do this:
SELECT [ID], CAST([ID] AS int) % 2 + 1
FROM Table1

Add null's for numerics using CTE in Sql Server 2005

I have some data as below
ID Data
1 a
1 2
1 b
1 1
2 d
2 3
2 r
Desired Output
ID Data
1 a
1 NULL
1 NULL
1 b
1 NULL
2 d
2 NULL
2 NULL
2 NULL
2 r
What the output is , for the numerics it is replaced with those many null values. E.g. for a numeric value of 2 , it will be 2 null values.
The ddl is as under
Declare #t table(ID int, data varchar(10))
Insert into #t
Select 1, 'a' union all select 1, '2' union all select 1, 'b' union all select 1, '1' union all select 2,'d' union all
select 2,'3' union all select 2, 'r'
select * from #t
Please give a CTE based solution. I already have the procedural approach but I need to have a feel of CTE based solution.
Solution I am using at present
CREATE FUNCTION [dbo].[SPLIT] (
#str_in VARCHAR(8000)
)
RETURNS #strtable TABLE (id int identity(1,1), strval VARCHAR(8000))
AS
BEGIN
DECLARE #tmpStr VARCHAR(8000), #tmpChr VARCHAR(5), #ind INT = 1, #nullcnt INT = 0
SELECT #tmpStr = #str_in
WHILE LEN(#tmpStr) >= #ind
BEGIN
SET #tmpChr = SUBSTRING(#tmpStr,#ind,1)
IF ISNUMERIC(#tmpChr) = 0
INSERT INTO #strtable SELECT #tmpChr
ELSE
WHILE #nullcnt < #tmpChr
BEGIN
INSERT INTO #strtable SELECT NULL
SET #nullcnt = #nullcnt + 1
END
SELECT #ind = #ind + 1, #nullcnt = 0
END
RETURN
END
GO
Invocation
SELECT * from dbo.SPLIT('abc2e3g')
I donot want to do it using function but a CTE solution.
Reason: I am learning CTE and trying to solve problems using it. Trying to come out of procedural/rBar approach.
Thanks
Setup:
declare #t table(UniqueID int identity(1,1), ID int, data varchar(10))
insert into #t
select 1, 'a' union all
select 1, '2' union all
select 1, 'b' union all
select 1, '1' union all
select 2, 'd' union all
select 2, '3' union all
select 2, 'r'
Query:
;with cte
as
(
select UniqueId, id, data, case when isnumeric(data) = 1 then cast(data as int) end Level
from #t
union all
select UniqueId, id, null, Level - 1
from cte
where Level > 0
)
select id, data
from cte
where Level is null or data is null
order by UniqueID, Level
Output:
id data
----------- ----------
1 a
1 NULL
1 NULL
1 b
1 NULL
2 d
2 NULL
2 NULL
2 NULL
2 r
I added UniqueId as there should be some kind of field that specifies order in the original table.
If you have a numbers table, this becomes quite simple.
;WITH Nbrs ( Number ) AS (
SELECT 1 UNION ALL
SELECT 1 + Number FROM Nbrs WHERE Number < 8000 )
SELECT
M.UniqueID, M.ID, Data
FROM
#t M
WHERE
ISNUMERIC(M.Data) = 0
UNION ALL
SELECT
M.UniqueID, M.ID, NULL
FROM
#t M
JOIN --the <= JOIN gives us (M.Data) rows
Nbrs N ON N.Number <= CASE WHEN ISNUMERIC(M.Data) = 1 THEN M.Data ELSE NULL END
ORDER BY
UniqueID
OPTION (MAXRECURSION 0)
Edit: JOIN was wrong way around. Oops.
Numbers CTE taken from here