Find which where clause is the most troublesome - sql

Problem background
I am trying to pin down to what condition(s) are causing no records / rows the most, so to allow me to find the root cause of what data in the database might need scrubbing.
So for example from the following query I would like to know whether it was the first condition which fails most of the time or second condition is the most offending one and so on.
SELECT TOP 1
FROM table
WHERE column1 = #param1 -- (cndtn 1)This condition works without anding with other conditions
AND column2 = #param2
AND column3 = #param3 -- (cndtn 3) This with 1 works 10% of the time
AND column4 = #param4
One of the ideas I thought was to break the procedure to use one condition at a time.
DECLARE #retVal int
SELECT #retVal = COUNT(*)
FROM table
WHERE column1 = #param1
IF (#retVal > 0)
--Do Something like above but by using #param2, #param3 and so on
Issues
If first check itself fails I wouldn't have a way forward to investigate into other combinations.
This doesn't seem very efficient either as this stored procedure is called hundreds of times.
Other SO Post I also find this great post (Find which one of the WHERE clauses succeeded) but this isn't very helping when no records are returned.

If this is just for debugging, what about detecting when the ##ROWCOUNT = 0 and storing those parameters in a separate debugging table?
SELECT TOP 1 *
FROM SomeTable
WHERE column1 = #param1
AND column2 = #param2
AND column3 = #param3
AND column4 = #param4
-- order by ....
;
-- one or more parameters "failed"
IF ##ROWCOUNT = 0
BEGIN
INSERT INTO SomeTable_Debug( createdDate, column1, column2, column3, column4, column5)
VALUES (getDate(), #param1, #param2, #param3, #param4, #param5)
END
You can then use the debugging table later on, in a separate query script, without having to worry about it's impact on a frequently invoked procedure. For example, this query returns 1 when a condition "fails", otherwise it returns null. It's not optimized for efficiency, but should be fine for occasional debugging use:
SELECT *
, (SELECT 1 WHERE NOT EXISTS (SELECT NULL FROM SomeTable st WHERE st.column1 = d.column1)) AS Matches_Column1
, (SELECT 1 WHERE NOT EXISTS (SELECT NULL FROM SomeTable st WHERE st.column2 = d.column2)) AS Matches_Column2
, (SELECT 1 WHERE NOT EXISTS (SELECT NULL FROM SomeTable st WHERE st.column3 = d.column3)) AS Matches_Column3
, (SELECT 1 WHERE NOT EXISTS (SELECT NULL FROM SomeTable st WHERE st.column4 = d.column4)) AS Matches_Column4
, (SELECT 1 WHERE NOT EXISTS (SELECT NULL FROM SomeTable st WHERE st.column5 = d.column5)) AS Matches_Column5
FROM SomeTable_Debug d
Sample Results:
id
createdDate
column1
column2
column3
column4
column5
Matches_Column1
Matches_Column2
Matches_Column3
Matches_Column4
Matches_Column5
1
2022-04-18 16:51:11.487
1
22
3
4
5
null
1
null
null
null
2
2022-04-18 16:51:11.500
1
22
3
4
56
null
1
null
null
1
db<>fiddle here

Related

Get row from table based on parameter

I want to get row from based on parameter value. Either it could be value 'ABC' or NULL. Below is the source table and expected result, which I'm trying to achieve.
SourceTable
column1 column2
--------------------------
value1 NULL
value2 ABC
Tried with query, but it is getting two rows which are with value1 and value2.
Declare #Param1 varchar(20) = 'ABC'
Select *
from SourceTable
where column2 = #Param1 Or column2 is NULL
If value is 'ABC' then Result -
column1 column2
--------------------------
value2 ABC
If value is NULL then Result -
column1 column2
--------------------------
value1 NULL
Perhaps this would work for you?
select *
from SourceTable
where column2 = #Param1 or (#Param1 is null and column2 is null)
You can try something like: Only problem you might encounter with this is if your column2 has blanks.
SELECT *
FROM SourceTable
WHERE ISNULL(column2, '') = ISNULL(#Param1, '')
Perhaps you want union all and a check for existence:
Select *
from SourceTable
where column2 = #Param1
union all
Select *
from SourceTable
where column2 is null and not exists (select 1 from sourcetable st2 where st2.column2 = #Param1);
An alternative uses order by -- if you want only one row:
select top 1 st.*
from sourcetable st
where column2 = #param1 or column2 is null
order by (case when column2 is not null then 1 else 2 end);

Only select rows if at least one exists

I have the following query:
IF EXISTS ( SELECT Column1 ,
Column2
FROM dbo.Table1
EXCEPT
SELECT Column1 ,
Column2
FROM #proposedData )
BEGIN
SELECT Column1 ,
Column2
FROM dbo.Table1
EXCEPT
SELECT Column1 ,
Column2
FROM #proposedData
RAISERROR('Unexpected values in proposed data',16,10)
END
I want to raise an error if the data that already exists in a table does not appear in a table variable. This is to make sure that my source code matches what is in a particular environment. The problem with this is that the two select queries are duplicate code. This introduces a possibility for human error - the two queries should be the same but could be different. An alternative is:
SELECT Column1 ,
Column2
FROM dbo.Table1
EXCEPT
SELECT Column1 ,
Column2
FROM #proposedData
IF ##ROWCOUNT <> 0
BEGIN
RAISERROR ('Invalid ObjectType values',16,10)
END
However this 'pollutes' the query output as there will be an empty result set if the data is correct. So, is there way to prevent a result set being output if there are 0 rows? An idea is a stored proc that takes the select and constructs the first example query from it but was wondering there was a built in way to do it.
Thanks
Joe
How about this ?
if exists(SELECT Column1 ,
Column2
FROM dbo.Table1
EXCEPT
SELECT Column1 ,
Column2
FROM #proposedData)
RAISERROR ('Invalid ObjectType values',16,10)

Sql server Query : Conditional Select columns with top keyword

DECLARE #mode INT;
SELECT CASE
WHEN #mode = 0
THEN t.Column1,Count(t.Column2) as Column2
ELSE top 1 t.Column1,Count(t.Column2) as Column2
END
FROM Table1 t
--Where some list of parameters
Group by t.Column1,t.Column2
Please read the above sql statement carefully. I have requirement to evaluate the query by two modes without changing the body of the query ie. From,Where and Group clauses should be written only once and not to replicate them (each one) anywhere in the result query
if #mode = 0 then the above said columns should be returned, and
if #mode <> 0 then the "Top1" of records should be returned
Both select conditions use the same given set list of parameters.
When I run the above query am facing the error "Incorrect syntax near the keyword 'top'." because we could not use the top 1 keyword within conditional select statements and select condition's columns must be matched even with their datatypes.
I need to fix the above query without affecting logic of the query.
IF(#mode <= 0)
BEGIN
Select Column1,Count(t.Column2) as Column2
From Table1 t
Group by t.Column1,t.Column2
END
Else
BEGIN
Select top 1 *
From Table1 t
END
Or create a temporary table and load data using where condition
Create Table #Temp (Column1 datetype,Column2 datetype)
Insert Into #Temp (Column1,Column2)
Select Column1,Column2
From Table1 t
Where condition
IF(#mode <= 0)
BEGIN
Select Column1,Count(t.Column2) as Column2
From #Temp t
Group by t.Column1,t.Column2
END
Else
BEGIN
Select top 1 *
From #Temp t
END
BEGIN
DECLARE #mode INT;
SET #mode = 0;
IF #mode <= 0
SELECT t.Column1,count(t.Column2) as Column2 from Table_Name t
GROUP BY t.Column1,t.Column2
ELSE
SELECT TOP 1 t.Column1,count(t.Column2) as Column2 from Table_Name t
GROUP BY t.Column1,t.Column2
END
DECLARE #mode INT;
Set #mode = 1 --for Min set of records
--Set #mode = 100 --for Max / full set of records
SELECT TOP (#var) PERCENT * t.Column1 ,Count(t.Column2) AS Column2 FROM Table1 t
--Where some list of parameters
GROUP BY t.Column1 ,t.Column2

Execute queries until non-empty result

I have three queries, looking like these:
SELECT * FROM Table1 WHERE Column1 = 'a'
SELECT * FROM Table2 WHERE Column2 = 'b'
SELECT * FROM Table1 A, Table2 B WHERE A.Column1 <> B.Column1
Now all logic is implemented on the client side as following. Execute the first query, if HasRows, set a flag to 1 and return the rows. Otherwise execute the second query, if HasRows, set the flag to 2 and return the rows. Otherwise execute the third query, set the flag to 3 and return the rows.
How to do this with a single query? Flag stuff, I guess, should be solved adding Flag to the queries:
SELECT Flag = 1, * FROM Table1 WHERE Column1 = 'a'
SELECT Flag = 2, * FROM Table2 WHERE Column2 = 'b'
SELECT Flag = 3, * FROM Table1 A, Table2 B WHERE A.Column1 <> B.Column1
But now what? How to check, if a query returns non-empty result?
Also, I'd like to cache the results, in other words, to avoid executing the same query twice - once for checking and the second time - for returning data.
Regards,
You could use a table variable to store the result and only return it at the end of the SQL block. Checking ##rowcount would tell you if the previous insert added any rows; if it's zero, you can run further queries:
declare #result table (flag int, col1 int, col2 varchar(50))
insert #result select 1, col1, col2 from Table1 where Column1 = 'a'
if ##rowcount = 0
begin
insert #result select 2, col1, col2 from Table2 where Column1 = 'b'
end
if ##rowcount = 0
begin
insert #result select 3, col1, col2 from Table1 A, Table2 B
where A.Column1 <> B.Column1
end
select * from #result
This approach only works if each select has the same column definition.

IF statement error

I have the following columns in TableA
TableA
Column1 varchar
Column2 int
Column3 bit
I am using this statement
IF Column3 = 0
SELECT Column1, Column2 FROM
TableA WHERE
Column2 > 200
ELSE
SELECT Column1, Column2 FROM
TableA WHERE
Column2 < 200
But the statment does not compile. It says Invalid Column Name 'Column3'
Column3 is not being referenced anywhere outside of the IF and ELSE blocks. If you wish to reference this value you will need to declare a new variable and use that;
DECLARE #btColumn3 BIT
SELECT #btColumn3 = Column3 FROM #tblTableA
IF #btColumn3 = 0
SELECT Column1, Column2 FROM
#tblTableA WHERE
Column2 > 200
ELSE
SELECT Column1, Column2 FROM
#tblTableA WHERE
Column2 < 200
Or do the following;
IF (SELECT Column3 FROM #tblTableA) = 0
SELECT Column1, Column2 FROM
#tblTableA WHERE
Column2 > 200
ELSE
SELECT Column1, Column2 FROM
#tblTableA WHERE
Column2 < 200
Either way you will have to ensure that the query used to retrieve Column3 returns a single result either by limiting your query so that it can only return a single value or using MIN(), MAX() etc depending on your requirements.
Also, if you need to execute more than one query within the IF and ELSE blocks you will need to wrap the contents in BEGIN and END as follows:
IF #btColumn3 = 0
BEGIN
// Do a query
// Do another
END
ELSE
BEGIN
// Do a query
// Do another
END
If you want to do this you need to first store the value of Column3 in a variable.
Declare #temp money
Select #Temp = Column3
From TableA
IF #Temp = 0
begin
SELECT Column1, Column2 FROM
TableA WHERE
Column2 > 200
end
ELSE
begin
SELECT Column1, Column2 FROM
TableA WHERE
Column2 < 200
end
Obviously, this assumes that there will only be one value returned for Column3.
EDIT:
This is a different approach which I think should work for you:
declare #CutOffValue money
declare #MaxValue money
Set #CutOffValue = 200
Set #MaxValue = 9999999999
Select Column1, Column2
From TableA
Where Column2 > Case When Column3 = 0 Then #CutOffValue Else 0 End
And Column2 < Case When Column3 = 0 Then #MaxValue Else #CutOffValue End
You are mixing 2 different levels:
IF is at the TSQL (procedure) level and cannot depend on the row values
SELECT is the query itself where the row values can be used to filter the result set
The following would work
IF Condition /* independent of the different values of TableA. can be an aggregate though */
BEGIN
SELECT Column1, Column2 FROM
TableA WHERE
Column2 > 200
END
ELSE
BEGIN
SELECT Column1, Column2 FROM
TableA WHERE
Column2 < 200
END
You will need the syntax as follows
IF <CONDITION>
BEGIN
<Your Statement>
END
ELSE
<Your Statement>
Hope this is helpful!!
Assuming you have posted the complete query, then the problem with your IF clause is that you are assuming that it can use the columns from the SELECT statement following it. It cannot and does not. This is why it will not compile.
You need your test condition to be separate from the statements following the IF clause. See on MSDN.
DECLARE #test BIT
SELECT #test = 0
IF #test = 0
BEGIN
SELECT Column1, Column2 FROM
TableA WHERE
Column2 > 200
END
ELSE
BEGIN
SELECT Column1, Column2 FROM
TableA WHERE
Column2 < 200
END
Why not just do
select Column1, Column2 from TableA where
Column2 > 200 and Column3 = 0 or Column2 < 200 and Column3 = 1
or, abusing arithmetics,
select Column1, Column2 from TableA where (Column2 - 200) * (2 * Column3 - 1) < 0
Since the answer to your problem varies largely on exactly what logic has to be implemented, analyzing present scenario all I can give you is a small and compact query which can meet your requirements (I hope so…)
SELECT Column1,column2 FROM TableA WHERE
(CASE WHEN Column3=0 then Column2 else 2)>(CASE WHEN Column3=0 then 200 ELSE 1)
AND (CASE WHEN Column3<>0 then Column2 else 1)<(CASE WHEN Column3<>0 then Column2 else 2)