SQL SELECT statement within an IF statement - sql

I have a trigger in SQL Server that needs to check on an update the number of rows with a value between a certain amount and do something accordingly. My current code is something like this:
IF EXISTS(SELECT COUNT(id) as NumberOfRows
FROM database
WHERE id = 3 AND value <= 20 and value > 2
GROUP BY id
HAVING COUNT(id) > 18)
-- if true, do something
From what I can tell, the select statement should find the number of rows with a value between 2 and 20 and if there are more than 18 rows, the EXISTS function should return 1 and the query will execute the code within the IF statement.
However, what is happening is that it is always executing the code within the IF statement regardless of the number of rows with a value between 2 and 20.
Any ideas on why this might be? I can post more complete code if it might help.

The reason is that the Exists function is checking the result of the sub-query for existing - are there any rows or not. And, as you return the COUNT, it'll never be not-existing - COUNT returns 0 if there are no rows presented in database.
Try to store the resulting count in a local variable, like in this question:
Using IF ELSE statement based on Count to execute different Insert statements
DECLARE #retVal int
SELECT #retVal = COUNT(*)
FROM TABLE
WHERE COLUMN = 'Some Value'
IF (#retVal > 0)
BEGIN
--INSERT SOMETHING
END
ELSE
BEGIN
--INSERT SOMETHING ELSE
END

I would do it like so (single line):
IF ((SELECT COUNT(id) FROM table WHERE ....)>18) BEGIN
...do something
You can even do between in a single line
IF ((SELECT COUNT(id) FROM table WHERE ....)between 2 and 20) BEGIN
...do something
END

Your subquery is looking for matches in the entire table. It does not limit the results only to those that are related to the rows affected by the update. Therefore, if the table already has rows matching your condition, the condition will be true on any update that affects other rows.
In order to count only the relevant rows, you should either join the database table to the inserted pseudo-table or use just the inserted table (there is not enough information in your question to be sure which is better).

Related

How to execute two queries based on case condition in sql server

I am trying to execute queries based on condition within single table,here is my query in sql server.
where PATTERN is column name and is set to 0 as default value
I want to display result into only one cell of excel sheet. I linked SQL server and Excel sheet.
IF ([PATTERN] = 1)
BEGIN
SELECT PATTERN,COLOR,SHIFT FROM [DEFECT_RESULTS]
END
ELSE
SELECT MODEL,COLOR FROM [DEFECT_RESULTS]
pattern column is present then still following error is displaying
error: Invalid column name 'PATTERN'.
You need to perform a query in the if condition. SQL doesn't know what table/function you're referring to as it stands.
Something like
if exists (select 1 from DEFECT_RESULTS where PATTERN = 1)
begin
…
Which will do the true side of the if if any row in DEFECT_RESULTS matches. You may need a more specific condition.
declare #pattern int
if (#pattern=1)
begin
SELECT PATTERN,COLOR,SHIFT FROM [DEFECT_RESULTS]
end
else if(#pattern=0)
begin
SELECT MODEL,COLOR FROM [DEFECT_RESULTS]
end
A single query cannot return 2 columns sometimes and 3 columns at other times. Perhaps you just want two queries:
SELECT PATTERN, COLOR, SHIFT
FROM [DEFECT_RESULTS]
WHERE PATTERN = 1;
SELECT MODEL, COLOR
FROM [DEFECT_RESULTS]
WHERE PATTERN = 0;

Show results from query if results exist without running the same query again

I want to have a stored procedure that will take one SerialNumber nvarchar as it's input and check several databases to see if that serial number exists and if it does exist then return the result of the query, otherwise move onto the next database and do the same thing until all databases have been checked.
Current pseudocode:
IF(exists(select top 1 * from Server1.Database1.Table where num = #SerialNumberInput))
BEGIN
select top 1 * from Server1.Database1.Table where num = #SerialNumberInput
END ELSE
IF(exists(select top 1 * from Server2.Database2.Table where num = #SerialNumberInput))
BEGIN
select top 1 * from Server2.Database2.Table where num = #SerialNumberInput
END ELSE
--Server3.Database3
--Server4.Database4
--etc...
But I don't like all this query repetition and I don't like how I'm having to make a call to the server twice by calling the same query twice. I could save the result to a table variable and just check that but that feels hacky.
Too long to comment.
But I don't like all this query repetition
Me neither, but for this case, it's the cleanest method or most readable IMHO.
I don't like how I'm having to make a call to the server twice by
calling the same query twice.
You aren't, at least not exactly. EXISTS returns a BOOLEAN value so as long as there is an INDEX on your predicate, it should be pretty fast. The second query, where you are returning the first row with all of the columns would be slightly slower. Also, you don't need top 1 * in the EXISTS unless you just like that. You can use SELECT 1 or anything since the result is BOOLEAN.
Another thing is you are using TOP without and ORDER BY which means you don't care what row is returned, and are OK with that row being different (potentially) each time you execute this. More on that in this blog.
If you really want to not use EXISTS, you can break this up using ##ROWCOUNT.
select top 1 * from Server1.Database1.Table where num = #SerialNumberInput
if ##ROWCOUNT = 1
return
else
select top 1 * from Server2.Database2.Table where num = #SerialNumberInput
if ##ROWCOUNT = 1
return
else
...
Or, if the schema is the same and you don't want NULL datasets... something like you said with the table variable.
create table #Temp(...)
insert into #Temp
select top 1 * from Server1.Database1.Table where num = #SerialNumberInput
if ##ROWCOUNT = 1
select * from #Temp
return
else
insert into #Temp
select top 1 * from Server2.Database2.Table where num = #SerialNumberInput
if ##ROWCOUNT = 1
select * from #Temp
return
else
...
Since you are only inserting a single row, it'd be pretty quick. Larger datasets would naturally take longer.

Is a subquery, which is returning no row, equal to NULL?

In SQL Server, if my SELECT statement in a subquery returns no row, is then the result of the subquery equal to NULL? I made some research, but I am not sure about it.
Example:
IF (SELECT TOP 1 CLMN1 FROM SOMETABLE) IS NOT NULL THEN
....
I am asking to understand the behaviour of the if-statement above.
Looks like the answer is yes:
DECLARE #Test TABLE (Id INT)
INSERT INTO #Test VALUES (1)
SELECT * FROM #Test WHERE Id = 2
SELECT CASE WHEN (SELECT * FROM #Test WHERE Id = 2) IS NULL THEN 1 ELSE 0 END
EDIT: after you updated your question I think I should add that instead of checking if there are rows with IS NULL you should use the following that can be better optimised by the server:
IF EXISTS(SELECT * FROM #Test WHERE Id = 2)
BEGIN
-- Whatever
END
NULL means no value, for example that the "box" for a certain column in a certain row is empty. NO ROW means that there are no rows.
No, NULL is a column value that indicates that the value of that column for a given row has no valid value. There would have to be a row returned by your query for that row to contain NULL column values.
A query that returns no rows just means that no rows matched the predicate you used in the query and therefore no data was returned at all.
Edit: After the question was edited, my answer doesn't address the specific case called out in the question. Juan's answer above does.

T-SQL cursor and update

I use a cursor to iterate through quite a big table. For each row I check if value from one column exists in other.
If the value exists, I would like to increase value column in that other table.
If not, I would like to insert there new row with value set to 1.
I check "if exists" by:
IF (SELECT COUNT(*) FROM otherTabe WHERE... > 1)
BEGIN
...
END
ELSE
BEGIN
...
END
I don't know how to get that row which was found and update value. I don't want to make another select.
How can I do this efficiently?
I assume that the method of checking described above isn't good for this case.
Depending on the size of your data and the actual condition, you have two basic approaches:
1) use MERGE
MERGE TOP (...) INTO table1
USING table2 ON table1.column = table2.column
WHEN MATCHED
THEN UPDATE SET table1.counter += 1
WHEN NOT MATCHED SOURCE
THEN INSERT (...) VALUES (...);
the TOP is needed because when you're doing a huge update like this (you mention the table is 'big', big is relative, but lets assume truly big, +100MM rows) you have to batch the updates, otherwise you'll overwhelm the transaction log with one single gigantic transaction.
2) use a cursor, as you are trying. Your original question can be easily solved, simply always update and then check the count of rows updated:
UPDATE table
SET column += 1
WHERE ...;
IF ##ROW_COUNT = 0
BEGIN
-- no match, insert new value
INSERT INTO (...) VALUES (...);
END
Note that this approach is dangerous though because of race conditions: there is nothing to prevent another thread from inserting the value concurrently, so you may end up with either duplicates or a constraint violation error (preferably the latter...).
This is just psuedo code because I have no idea of your table structure but I think you will understand... basically Update the columns you want then Insert the columns you need. A Cursor operation sounds unnecessary.
Update OtherTable
Set ColumnToIncrease = ColumnToIncrease + 1
FROM CurrentTable Where ColumnToCheckValue is not null
Insert Into OtherTable (ColumnToIncrease, Field1, Field2,...)
SELECT
1,
?
?
FROM CurrentTable Where ColumnToCheckValue is not null
Without a sample, I think this is the best I can do. Bottom line: you don't need a cursor. UPDATE where a match exists (INNER JOIN) and INSERT where one does not.
UPDATE otherTable
SET IncrementingColumn = IncrementingColumn + 1
FROM thisTable INNER JOIN otherTable ON thisTable.ID = otherTable.ID
INSERT INTO otherTable
(
ID
, IncrementingColumn
)
SELECT ID, 1
FROM thisTable
WHERE NOT EXISTS (SELECT *
FROM otherTable
WHERE thisTable.ID = otherTable.ID)
I think you'd be better off using a view for this -- then it's always up to date, no risk of mistakenly double/triple/etc counting:
CREATE VIEW vw_value_count AS
SELECT st.value,
COUNT(*) AS numValue
FROM SOME_TABLE st
GROUP BY st.value
But if you still want to use the INSERT/UPDATE approach:
IF EXISTS(SELECT NULL
FROM SOMETABLE WHERE ... > 1)
BEGIN
UPDATE TABLE
SET count = count + 1
WHERE value = #value
END
ELSE
BEGIN
INSERT INTO TABLE
(value, count)
VALUES
(#value, 1)
END
What about Update statement with inner join to perform +1, and Insert selected rows that do not exist in the first table.
Provide the tables schema and the columns you want to check and update so I can help.
Regards.

Check whether a table contains rows or not sql server 2005

How to Check whether a table contains rows or not sql server 2005?
For what purpose?
Quickest for an IF would be IF EXISTS (SELECT * FROM Table)...
For a result set, SELECT TOP 1 1 FROM Table returns either zero or one rows
For exactly one row with a count (0 or non-zero), SELECT COUNT(*) FROM Table
Also, you can use exists
select case when exists (select 1 from table)
then 'contains rows'
else 'doesnt contain rows'
end
or to check if there are child rows for a particular record :
select * from Table t1
where exists(
select 1 from ChildTable t2
where t1.id = t2.parentid)
or in a procedure
if exists(select 1 from table)
begin
-- do stuff
end
Like Other said you can use something like that:
IF NOT EXISTS (SELECT 1 FROM Table)
BEGIN
--Do Something
END
ELSE
BEGIN
--Do Another Thing
END
FOR the best performance, use specific column name instead of * - for example:
SELECT TOP 1 <columnName>
FROM <tableName>
This is optimal because, instead of returning the whole list of columns, it is returning just one. That can save some time.
Also, returning just first row if there are any values, makes it even faster. Actually you got just one value as the result - if there are any rows, or no value if there is no rows.
If you use the table in distributed manner, which is most probably the case, than transporting just one value from the server to the client is much faster.
You also should choose wisely among all the columns to get data from a column which can take as less resource as possible.
Can't you just count the rows using select count(*) from table (or an indexed column instead of * if speed is important)?
If not then maybe this article can point you in the right direction.
Fast:
SELECT TOP (1) CASE
WHEN **NOT_NULL_COLUMN** IS NULL
THEN 'empty table'
ELSE 'not empty table'
END AS info
FROM **TABLE_NAME**