SQL Server : EXISTS (SELECT INTO) - tsql

I have 3 queries. If the first one returns empty set, I execute the second query, and if it returns empty set too, I give to the server the last chance and do the third:
SELECT ... INTO #Query1
IF EXISTS(SELECT * FROM #Query1)
SELECT * FROM #Query1
ELSE BEGIN
SELECT ... INTO #Query2
IF EXISTS(SELECT * FROM #Query2)
SELECT * FROM #Query2
ELSE BEGIN
SELECT ...
END
END
It worked well, but I've faced a new task - the query should be used as a subquery as well as other queries, within IF EXISTS([subquery]). Doing this, I see
Incorrect syntax near the keyword 'INTO'.
How should I modify the original query to meet this new condition?
Regards,

Are you looking for something like this:
create function dbo.Foo()
returns #Result table ( ThingId Int, Source Int )
as
begin
insert into #Result
select ThingId, 1 as Source from Things1
if ##ROWCOUNT = 0
insert into #Result
select ThingId, 2 as Source from Things2
if ##ROWCOUNT = 0
insert into #Result
select ThingId, 3 as Source from Things3
--...
return
end
go
select * from dbo.Foo()
select 42 as 'The Answer' where exists ( select * from dbo.Foo() where Source > 9 )
You can pass parameters into the function to use in WHERE clauses and the like. The Source column may be omitted if you don't care to know where the data originated.

Related

Postgres SQL | IF ELSE | HOW TO

I am using psql (PostgreSQL) 11.2 (Debian 11.2-1.pgdg90+1).
I am trying to write a logic in .PSQL file that needs to import some data into a table if this table is empty, else do something else.
I am struggling to find the correct syntax to make it work.
Would appreciate some help around this.
DO $$ BEGIN
SELECT count(*) from (SELECT 1 table_x LIMIT 1) as isTableEmpty
IF isTableEmpty > 0
THEN
INSERT INTO table_x
SELECT * FROM table_b;
ELSE
INSERT INTO table_y
SELECT * FROM table_b;
END IF;
END $$;
thanks!
Read plpgsql structure. Then you would know you need a DECLARE section to declare isTableEmpty and from here Select into that you need to select into the isTableEmpty variable. So:
...
DECLARE
isTableEmpty integer;
BEGIN
SELECT count(*) into isTableEmpty from (SELECT 1 table_x LIMIT 1);
...
Though I'm not sure what you are trying to accomplish with?:
SELECT count(*) from (SELECT 1 table_x LIMIT 1) as isTableEmpty
As that is always going to return 1.
You are using count just to determine that a row exists or not in the table. To do so you need to create a variable in the DO block, select into that variable, and reference that variable. This is all unnecessary; you can just use exists(...) instead of count(*) .... See demo;
do $$
begin
if not exists (select null from table_x) then
insert into table_x (...)
values (...);
else
insert into table_y (...)
values (...);
end if;
end ;
$$;

Insert into ##temptable with IF condition

I have to change a procedure in such a way to make it dependable on user ID. The procedure includes several SELECT INTO statements and SELECT statement at the end, which returns data.
I wanted to add IF condition which will check user ID and execute SELECT INTO statement for this condition. However the problem is that there can't be the same tables in two conditions.
One can't execute this code:
select '1' as Number into ##temp
if 1=1
begin
select * into ##temp2 from ##temp
end
else
begin
select * into ##temp2 from ##temp
end
Could you just create the structure of the second table first, then insert into it accordingly?
select '1' as Number into ##temp
select * into ##temp2 from ##temp where 1=0
if 1=1
begin
insert into ##temp2 select * from ##temp
end
else
begin
insert into ##temp2 select * from ##temp
end

TSQL break loop when ##ROWCOUNT = 0

I have insert statements (simplified) in a SPROC like the following
SET ROWCOUNT 100
WHILE(1=1)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
-- EDIT: Realized forgot to include this following vital line that is causing issue
SET #var = #var + ##ROWCOUNT
-- ##ROWCOUNT now takes on a value of 1, which will cause the following IF check to fail even when no lines are inserted
IF(##ROWCOUNT = 0)
BEGIN
BREAK
END
END
But the issue is, after any operation even when no more rows fit my some_condition, ##ROWCOUNT is equal to 1, not 0.
How can I break that loop when there are 0 rows returned matching my some_condition?
The "set" statement creates a row count of 1. What you should do is immediately save ##ROWCOUNT into a #rowCount variable and use that var later on.
declare #rowCount int
WHILE(1=1)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
-- EDIT: Realized forgot to include this following vital line that is causing issue
SET #rowCount = ##ROWCOUNT
SET #var = #var + #rowCount
-- ##ROWCOUNT now takes on a value of 1, which will cause the following IF check to fail even when no lines are inserted
IF(#rowCount = 0)
BEGIN
BREAK
END
END
Also, you can simplify by setting #rowCount to -1 initially and changing the WHILE condition to #rowCount <> 0. The conditional BREAK will no longer be needed.
An alternative solution. This checks each iteration to see if the ID of the last inserted record has changed or not. If it hasn't changed, it indicates that no records were added that iteration.
SET ROWCOUNT 100
declare #id int;
WHILE(1=1)
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
IF(#id= ##identity)
BEGIN
BREAK
END
set #id = ##identity;
END
Try this solutions:
1st solution
Using ##ROWCOUNT in loop's condition.
SET ROWCOUNT 100
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
WHILE(##ROWCOUNT > 0)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
END
2nd solition
Using goto.
SET ROWCOUNT 100
WHILE(1=1)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
IF(##ROWCOUNT = 0)
BEGIN
goto label
END
END
label1:
print 'After lopp'
I think you should use select to get the ##rowcount into a variable. try this:
declare #number_of_rows int
SET ROWCOUNT 100
WHILE(1=1)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
SELECT #number_of_rows=##ROWCOUNT
IF (#number_of_rows = 0)
BEGIN
BREAK
END
END
Implemented solution similar to Moho, but used SELECT instead of SET to store ##ROWCOUNT.

Getting Top N from stored procedure

I have a stored procedure that cannot be modified, I'm going to stress this before anyone suggests I re-write the stored procedure or add the query from inside the stored procedure into a function.
The procedure lives on another database that we have very limited access to; so what I want to do is somehow wrap the stored procedure in a query, function or stored procedure that will allow me to select the top N rows from the returned data.
Ideally I would be able to call something like...
DECLARE #ForeName varchar(50)
DECLARE #Surname varchar(50)
DECLARE #DOB datetime
DECLARE #Sex varchar(1)
SET #Surname = 'Smith'
SELECT TOP 10 (
EXECUTE #RC = [Some_Other_Database].[dbo].[sp_search_demographics]
,#ForeName
,#Surname
,#DOB
,#Sex
)
GO
edit: (I should also note that the stored procedure returns a parameter containing the row count as well as the rows)
edit2: I should also note that I'm using MS SQL Server 2008 R2
I'm aware that this is in no way correct, is there any way to do something like this? at the moment for vague queries we are getting thousands of rows returned; which is slowing the server considerably.
I have done some Googling and stack-overflowing for a solution but unfortunately all the advice I could find involved modifying the stored procedure.
Look up EXEC SP_EXECUTESQL(#SQL)
However the problem will be that the called sp will still return all the rows, so you may not get the improvement in performance you are looking for.
You can also set the number of rows returned by a query - but depends on your access level
http://blog.sqlauthority.com/2007/04/30/sql-server-set-rowcount-retrieving-or-limiting-the-first-n-records-from-a-sql-query/
Hope this helps
Declare #i Numeric(18,2)
Declare #strSQL nvarchar(1000)
select #i = Round(COUNT(1)/10,2) from tb_Item
print(#i)
Declare #j int = 0
Declare #rem numeric(18,2)
select #rem = COUNT(1) - ((COUNT(1)/10) * 10) from tb_Item
while #i > 0
Begin
set #j = (#j + 1);
if #j = 1
Begin
WITH OrderedOrders AS
(
select
ROW_NUMBER() over(order by ItemID) AS RowNumber
,ItemName
from tb_Item
)
SELECT ItemName, RowNumber
FROM OrderedOrders
WHERE RowNumber BETWEEN (#j*10)-10 AND #j*10;
End
Else
Begin
WITH OrderedOrders AS
(
select
ROW_NUMBER() over(order by ItemID) AS RowNumber
,ItemName
from tb_Item
)
SELECT ItemName, RowNumber
FROM OrderedOrders
WHERE RowNumber BETWEEN ((#j*10)-10) + 1 AND #j*10;
End
set #i = #i - 1;
end;
WITH OrderedOrders AS
(
select
ROW_NUMBER() over(order by ItemID) AS RowNumber
,ItemName
from tb_Item
)
SELECT ItemName, RowNumber
FROM OrderedOrders
WHERE RowNumber BETWEEN (#j*10)+1 and (#j*10) + #rem ;

Reading inserted column names and values in a TSQL trigger

I've been asked to create history tables for every table in a database. Then create a trigger that will write to the history table whenever the primary table is updated.
The history tables have the same structure as the primary table, but with a couple of extra rows ('id' and 'update type')
I've never done anything with triggers before, but I would like to do is dynamically go through the columns in 'Inserted' and construct an insert statement to populate the history table.
However I cannot work out how to read the names of the columns and their individual values.
My half finished trigger currently looks like...
CREATE TRIGGER tr_address_history
ON address
FOR UPDATE
AS
DECLARE #colCount int
DECLARE #maxCols int
SET #colCount = 0
SET #maxCols = (SELECT COUNT(column_name) FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'Inserted')
PRINT 'Number of columns = ' + CONVERT(varChar(10),#maxCols)
WHILE (#colCount <= #maxCols)
BEGIN
DECLARE #name varchar(255)
SELECT #name = column_name FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'Inserted'
DECLARE #value varchar(255)
SELECT #value = #name FROM Inserted
PRINT 'name = ' + #name + ' and value = ' + #value
SET #colCount = #colCount + 1
END
PRINT 'Done';
When the trigger runs it just says "Number of columns = 0"
Can anyone tell me what's wrong with :
SELECT COUNT(column_name) FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'Inserted'
Thanks...
First solution proposed by Beenay25 is good, but you should use affected table instead of 'inserted' pseudotable.
This is:
SELECT #name = column_name FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'AFFECTED_TABLE'
Instead of 'INSERTED'
Also, you should use dynamic SQL.
This will be a complete working solution:
ALTER TRIGGER [dbo].[tr_address_history]
ON [dbo].[address]
AFTER Insert
AS
DECLARE #ColumnName nvarchar(500)
DECLARE #TableName nvarchar(500)
DECLARE #value nvarchar(500)
DECLARE #Sql nvarchar(500)
Set #TableName='address'
DECLARE ColumnsCursor CURSOR FOR
select column_name FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'address'
OPEN ColumnsCursor
FETCH NEXT FROM ColumnsCursor into #ColumnName
WHILE ##FETCH_STATUS=0
BEGIN
select * into #tmp from inserted
Set #Sql= 'SELECT #value =' + #ColumnName + ' FROM #tmp'
EXEC sp_executesql #Sql, N'#Value nvarchar(500) OUTPUT', #Value OUTPUT
DROP TABLE #TMP
print '[' + #ColumnName +'='+ ltrim(rtrim(#Value))+']'
FETCH NEXT FROM ColumnsCursor into #ColumnName
END
CLOSE ColumnsCursor
DEALLOCATE ColumnsCursor
The 'inserted' table is a pseudo-table; it doesn't appear in INFORMATION_SCHEMA.
There is the UPDATE() operator for use in triggers:
CREATE TRIGGER trigger_name ON tablename
FOR UPDATE
AS
SET NOCOUNT ON
IF (UPDATE(Column1) OR UPDATE(Column2))
BEGIN
your sql here
END
COLUMNS_UPDATED
UPDATE()
There is a way to do what the questioner requires:
I have made something inside a trigger that tests whether all the columns of a particular table actually participated in an insert to that table. If they did, I later copied them to a history table. If they did not, then rollback and print only complete rows may be inserted into the report table. Perhaps they could adapt this to their needs:
here it is:
[
if exists (select 1 from inserted) and not exists (select 1 from deleted) -- if an insert has been performed
begin -- and we want to test whether all the columns in the report table were included in the insert
declare #inserted_columncount int, #actual_num_of_columns int, #loop_columns int, #current_columnname nvarchar(300),
#sql_test nvarchar(max), #params nvarchar(max), #is_there bit
set #actual_num_of_columns = (
select count(*) from (
select COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'renameFilesFromTable_report') as z)
set #inserted_columncount = 0
set #loop_columns = 1
declare inserted_columnnames cursor scroll for -- these are not really the inserted ones, but we are going to test them 1 by 1
select COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'renameFilesFromTable_report'
set #params = '#is_there_in bit output'
open inserted_columnnames
fetch next from inserted_columnnames into #current_columnname
select * into #temp_for_dynamic_sql from inserted -- this is necessary because the scope of sp_executesql does not include inserted pseudo table
while (#loop_columns <= #actual_num_of_columns) -- looping with independent integer arithmetic
begin
set #sql_test = '
set #is_there_in = 0
if exists (select ['+#current_columnname+'] from #temp_for_dynamic_sql where ['+#current_columnname+'] is not null)
set #is_there_in = 1'
exec sp_executesql #sql_test, #params, #is_there output
if #is_there = 1
begin
fetch next from inserted_columnnames into #current_columnname
set #inserted_columncount = #inserted_columncount + 1
set #loop_columns = #loop_columns + 1
end
else if #is_there <> 1
begin
fetch next from inserted_columnnames into #current_columnname
set #loop_columns = #loop_columns + 1
end
end
close inserted_columnnames
deallocate inserted_columnnames
-- at this point we hold in two int variables the number of columns participating in the insert and the total number of columns
]
Then you can simply do if #inserted_columncount < #actual_num_of_columns ..........
I did this because i have a sp that inserts 1 complete line to the report table every time it runs. That's fine, but i don't want anyone else touching that table by mistake. not even myself. I also want to keep history. So i made this trigger to keep the history but also to check if an insert was attempted without values for all the columns in the report table, and further down the code it checks if an update or delete was attempted and it rollbacks.
i was thinking of expanding this to allow an update but in which all the columns are set.
this could possibly be done as follows:
if update was attempted,
and exists (
select possibly_excluded.COLUMN_NAME from (
select COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'renameFilesFromTable_report') as possibly_excluded
group by possibly_excluded.COLUMN_NAME
having COLUMN_NAME not in (
select COLUMN_NAME
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = 'renameFilesFromTable_report' and
sys.fn_IsBitSetInBitmask(#ColumnsUpdated, COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + TABLE_NAME), COLUMN_NAME, 'ColumnID')) <> 0)
)
begin
rollback transaction
print 'Only updates that set the values for a complete row are allowed on the report table..'
end