Only select rows if at least one exists - sql

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)

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

SQL-using if exist in matching two columns in table

im trying to use keywords like detergent, soap, dish etc to match two column in my sql table, if the keywords find match in two column, i want to have another column saying its a matched. i am planning to use the if exist but i do not know the proper syntax.
sample column:
Column1 Column2
-----------------------------------------------
detergent powder all powder detergent
dish washing liquid dish liquid for washing
hand soap hand liquid soap
Here is the simplest solution to your question. The trick is in the "virtual" column, aliased as Match, that we create in the select statement. This column is computed using a case statement to see if the search term appears in both of the columns. Note we need to use the like statement with wildcard operators %.
create table Example (Column1 varchar(max), Column2 varchar(max));
insert into Example select 'detergent powder', 'all powder detergent';
insert into Example select 'dish washing liquid', 'dish liquid for washing' ;
insert into Example select 'hand soap', 'hand liquid soap';
declare #search varchar(20) = 'detergent';
select Column1,
Column2,
case when Column1 like '%' + #search + '%' and
Column2 like '%' + #search + '%'
then 'matched'
else 'not matched' end as [Match]
from Example;
We could also create the Match column as a "real" column in the table and modify this script slightly to update that column based on the same criteria.
Here's an example that checks if any of the 3 words appears in both columns.
Sample data:
CREATE TABLE Test (
Id INT IDENTITY(1,1) PRIMARY KEY,
Col1 VARCHAR(100),
Col2 VARCHAR(100)
);
INSERT INTO Test (Col1, Col2) VALUES
('detergent powder', 'all powder detergent'),
('dish washing liquid', 'dish liquid for washing'),
('hand soap', 'hand liquid soap'),
('soap dish', 'detergent');
Query:
SELECT t.*
, cast(
case
when exists (
select 1
from (values ('soap'),('detergent'),('dish')) s(search)
join (values (Col1),(Col2)) c(col)
on c.col like '%'+s.search+'%'
group by s.search
having count(*) = 2
) then 1 else 0 end as bit) as hasMatch
FROM Test t;
An EXISTS checks if there's at least 1 result from a query.
And the HAVING clause makes sure that 2 matches per search words are needed.
But it can also be done without that GROUP BY & HAVING clause:
SELECT t.*
, cast(case when exists (
select 1
from (values ('soap'),('detergent'),('dish')) s(search)
where Col1 like '%'+s.search+'%'
and Col2 like '%'+s.search+'%'
) then 1 else 0 end as bit) as hasMatch
FROM Test t;
A test on rextester here

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

Usage of COALESCE for no rows returned

I have used COALESCE numerous times but I suppose I never encountered this particular situation. Assume there is only one row per #param1.
select #test = COALESCE (column1, 'default') FROM Table1 WHERE column3 = #param1
In this scenario, if the value of column1 is null, 'default' is selected when the sql statement actually returns a row. What if there are no matching records for #param1.
Lets say I want to always have a default value for #test. Is the following correct or is there another way?
select #test = COALESCE( (select column1 FROM Table1 WHERE column3 = #param1), 'default').
I presumed that, select #test = COALESCE (column1, 'default') FROM Table1 WHERE column3 = #param1, will contain 'default' even if it did not return a row. Nope.
I suppose I can also check if #test is NULL afterwards and assign a default value as well.
You already effectively mentioned the answer... Use COALESCE after/outside the SELECT, as otherwise you never actually assign a value (which is different from assigning a NULL value)...
SELECT #test = NULL
SELECT #test = column1 FROM Table1 WHERE column3 = #param1
SELECT #test = COALESCE(#test, 'default')
Or simply...
SELECT #test = COALESCE((SELECT column1 FROM Table1 WHERE column3 = #param1), 'default')
You could also just give the variable the default value at declaration. If no rows are returned by the query no assignment will be made.
DECLARE #test VARCHAR(10) = 'default'
SELECT #test = COALESCE(column1, #test) /*Might not need COALESCE
if column1 is not nullable*/
FROM Table1
WHERE column3 = #param1
SELECT #test

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)