Execute queries until non-empty result - sql

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.

Related

Find which where clause is the most troublesome

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

Select default if no rows return

SELECT Column1
FROM Table1
WHERE PKColumn = SomeValue
I am selecting just one column, my query will return only 0 or 1 row for sure. I want to select some default values like Some Default if no row returned otherwise the returned value.
I tried something like
SELECT CASE WHEN COUNT(1) = 1 THEN Column1 ELSE 'Some Default' END AS Column1
FROM Table1
WHERE PKColumn = SomeValue
GROUP BY Column1
But it doesn't work.
Is there any way to do it in single SQL statement?
I think you can use a query like this:
SELECT TOP(1)
CASE WHEN EXISTS(SELECT 1 FROM t WHERE PKColumn = SomeValue) THEN Column1
ELSE 'Some Default' END AS Column1
FROM t;
Or using EXISTS with UNION:
SELECT 'Some Default' As Column1
WHERE NOT EXISTS(SELECT 1 FROM t WHERE PKColumn = SomeValue)
UNION ALL
SELECT Column1
FROM t
WHERE PKColumn = SomeValue;
You can use COALESCE
Evaluates the arguments in order and returns the current value of the
first expression that initially does not evaluate to NULL.
SELECT COALESCE((SELECT TOP 1 Column1 FROM Table1 WHERE PK = SomeValue), 'DefaultValue')
or like this:
DECLARE #ReturnValue INT = 3 -- Default value
SELECT TOP 1 #ReturnValue = Column1 FROM Table1 WHERE PK = SomeValue
SELECT #ReturnValue
SELECT
[Column1] = ISNULL([t2].[Column1], 'Some Default')
FROM
[Table1] AS [t1]
OUTER APPLY
(
SELECT
[Column1]
FROM
[Table1]
WHERE
[PKColumn] = [t1].[PKColumn]
) AS [t2]
WHERE
[t1].[PKColumn] = 'SomeValue';
Perhaps can try using LEN instead of COUNT to check if Column1 has value?
SELECT CASE WHEN LEN(Column1) > 0 THEN Column1 ELSE 'Some Default' END AS Column1
FROM Table1
WHERE PKColumn = SomeValue
GROUP BY Column1

for loop in transact sql

I have a table that contains some data that needs to be updated. Let's assume that the table structure is
(Code, Col1, Col2, Col3)
I need to run an update on specific rows that have the Code value (let's say the values are (1,3,4,5,9,6,30,25,87,100)).
The Col3 value is Col1+Col2 and the Col1 values are (1001,1025,400,25,963,13,432,25,87,100).
I created the following SQL Query:
Declare #Col1 float
Declare #Code nvarchar
set #Col1 = 1001
set #Code = 1
update MyTable set
Col1 = #Col1,
Col3 = #Col1 + Col2
where Code = #Code
So, instead copying all this code after the Declare lines and manually assigning values, is it possible to create two arrays, one for Col1 values other for Code values and iterate through the Code and updating it dynamically?
Usually iteration over rows of data using loops or cursors considered as bad practice in SQL since it is much slower in most cases.
In your particular case there is no need to iterate over some "arrays" to perform your desired update.
Instead you can create temporary table like this:
create table #temp_table (Col1 float, Code nvarchar(10))
fill it with your data like:
insert into #temp_table (Col1, Code)
select 1001, '1'
union all
select 1025, '3'
... and so on
and then perform your update:
update MyTable set
Col1 = T1.Col1,
Col3 = T1.Col1 + Col2
from MyTable as T
inner join #temp_table as T1 on T.Code = T1.Code
You don't need a loop for this, you can just create a Cte (or temp table) with the values you want to update, and JOIN to it in an UPDATE statement:
;With ToUpdate (Code, Col1) As
(
Select 1, 1001 Union All
Select 3, 1025 Union All
Select 4, 400 Union All
Select 5, 25 Union All
Select 9, 963 Union All
Select 6, 13 Union All
Select 30, 432 Union All
Select 25, 25 Union All
Select 87, 87 Union All
Select 100, 100
)
Update T
Set Col1 = U.Col1,
Col3 = U.Col1 + Col2
From MyTable T
Join ToUpdate U On U.Code = T.Code

Checking If Data in One Table Is Contained in Another

declare #q table (A int);
declare #a table (B int);
insert into #q select 1 union select 2 union select 3;
insert into #a select 0 union select 1 union select 2 union select 3 union select 4;
I want to know if the data of #q's column A is a subset of #a's column B.
This produces an error.
if (select A from #q) in (select B from #a)
print 'yes' else print 'no';
This works, but is it the best way of finding out?
if (select count(*) from #q) = (select count(*) from #q inner join #a on A = B)
print 'yes' else print 'no';
Or is there a better way?
Try using EXCEPT.
SELECT ColumnA
FROM TableA
EXCEPT
SELECT ColumnB
FROM TableB
It will give you a list of everything that's in A that's not in B.
You can insert the result from the above into a table variable, and then check its COUNT (0 = subset, anything else is not subset).
Try following. This basically looks for anything that is in #q but not in #a and gets its count. If the count is more than 0 then it returns No otherwise Yes.
SELECT CASE
WHEN COUNT(*) > 0 THEN 'No'
ELSE 'Yes'
END
FROM #q q
LEFT JOIN #a a
ON q.A = a.B
WHERE a.B iS NULL
Hope it helps.

Statement blocks in SQL SELECT using IF ELSE

I'm trying to return different data depending on a variable in a SELECT. Something like this:
SELECT
IF #variable = 'YES'
column1, column2
ELSE
column3
FROM TABLE
What is this the proper way to use the IF condition in SQL? Or is there a better alternative to what I'm trying to accomplish?
If you want to return a different number of columns, you'll need to use an IF:
IF #variable = 'YES'
BEGIN
SELECT column1, column2
FROM YourTable
END
ELSE
BEGIN
SELECT column3
FROM YourTable
END
If you want different data on the same column (assuming the same datatype), you could use a CASE:
SELECT CASE WHEN #variable = 'YES' THEN column1 ELSE Column2 AS Data
FROM YourTable
You can use an IF statement, but you'll need to set up multiple queries. You can use a CASE for selecting one column or another, but not to select one or multiple like in your question.
DECLARE #var INT = 1;
DECLARE #test TABLE (
Col1 int,
Col2 int,
Col3 int
)
INSERT INTO #test VALUES (1,2,3)
IF #var = 1
BEGIN
SELECT Col1, Col2
FROM #test
END
ELSE
BEGIN
SELECT Col3
FROM #test
END