i am using if else statement and ,i want to drop the temporary table which is created out side the inner if statement , but when i execute the statement , i get the answer as , There is already an object named '#table' in the database. ---------------------MY CODE IS LIKE enter code here
DECLARE #RowCount INT;
SET #RowCount = 1;
IF #RowCount = 1
BEGIN
IF OBJECT_ID('tempdb..#Guarantor_Details') IS NOT NULL
DROP TABLE #Guarantor_Details
SELECT DISTINCT TOP 1 GUARANTORS.GUARANTOR_CODE
INTO #Guarantor_Details
FROM GUARANTORS
WHERE ISNULL(GUARANTORS.deleted, 0) <> 1;
DECLARE #GuarantorCount INT;
SELECT #GuarantorCount = count(*)
FROM #Guarantor_Details;
IF (#GuarantorCount = 0)
DROP TABLE #Guarantor_Details
BEGIN
IF OBJECT_ID('tempdb..#Guarantor_Details') IS NOT NULL
SELECT TOP 1 CLIENT.Last_Name
INTO #Guarantor_Details
FROM CLIENT
LEFT JOIN GUARANTORS ON GUARANTORS.GUARANTOR_CODE = CLIENT.Financial_Guarantor
END
END
------------------------------------------------------------------------
You don't need to do this type of CREATE / DROP. You can (and probably should) create the table before the loop and simply either TRUNCATE TABLE or DELETE FROM the temp table when necessary to "reset" it. Just pick a datatype that fits both GUARANTOR_CODE and Last_Name.
You also don't need to do a SELECT COUNT(*) on the table when you have the ##ROWCOUNT from the INSERT operation that already has that value in it.
Essentially:
CREATE TABLE #Guarantor_Details (Value NVARCHAR(50));
WHILE (something)
BEGIN
IF (#RowCount = 1)
BEGIN
TRUNCATE TABLE #Guarantor_Details;
INSERT INTO #Guarantor_Details (Value)
SELECT DISTINCT TOP 1 GUARANTORS.GUARANTOR_CODE
FROM GUARANTORS
WHERE ISNULL(GUARANTORS.deleted, 0) <> 1;
IF (##ROWCOUNT = 0)
BEGIN
INSERT INTO #Guarantor_Details (Value)
SELECT TOP 1 CLIENT.Last_Name
FROM CLIENT
LEFT JOIN GUARANTORS
ON GUARANTORS.GUARANTOR_CODE = CLIENT.Financial_Guarantor;
END;
END;
FYI, there appear to be some logic problems in the original code:
There are BEGIN / END tags at the bottom of the IF #RowCount = 1 block that aren't associated to any IF or WHILE. This is technically ok, but usually implies something is missing.
If this code is all of the code, then the IF #RowCount = 1 doesn't work in the loop unless something sets #RowCount past the end of this outer IF block that is not shown here in the question.
Related
I am trying to do an update on a specific record every 1000 rows using Postgres. I am looking for a better way to do that. My function is described below:
CREATE OR REPLACE FUNCTION update_row()
RETURNS void AS
$BODY$
declare
myUID integer;
nRow integer;
maxUid integer;
BEGIN
nRow:=1000;
select max(uid_atm_inp) from tab into maxUid where field1 = '1240200';
loop
if (nRow > 1000 and nRow < maxUid) then
select uid from tab into myUID where field1 = '1240200' and uid >= nRow limit 1;
update tab
set field = 'xxx'
where field1 = '1240200' and uid = myUID;
nRow:=nRow+1000;
end if;
end loop;
END; $BODY$
LANGUAGE plpgsql VOLATILE
How can I improve this procedure? I think there is something wrong. The loop does not end and takes too much time.
To perform this task in SQL, you could use the row_number window function and update only those rows where the number is divisible by 1000.
Your loop doesn't finish because there is no EXIT or RETURN in it.
I doubt you could ever rival the performance of a standard SQL update with a procedural loop. Instead of doing it a row at a time, just do it all as a single statement:
with t2 as (
select
uid, row_number() over (order by 1) as rn
from tab
where field1 = '1240200'
)
update tab t1
set field = 'xxx'
from t2
where
t1.uid = t2.uid and
mod (t2.rn, 1000) = 0
Per my comment, I am presupposing what you mean by "every 1000th row," as without some designation of how to determine what tuple is what row number. That is easily edited by changing the "order by" criteria.
Adding a second where clause on the update (t1.field1 = '1240200') can't hurt but might not be necessary if these are nested loop.
This might be notionally similar to what Laurenz has in mind.
I solved this way:
declare
myUID integer;
nRow integer;
rowNum integer;
checkrow integer;
myString varchar(272);
cur_check_row cursor for select uid , row_number() over (order by 1) as rn, substr(fieldxx,1,244)
from table where field1 = '1240200' and uid >= 1000 ORDER BY uid;
BEGIN
open cur_check_row;
loop
fetch cur_check_row into myUID, rowNum, myString;
EXIT WHEN NOT FOUND;
select mod(rowNum, 1000) into checkrow;
if checkrow = 0 then
update table
set fieldxx= myString||'O'
where uid in (myUID);
end if;
end loop;
close cur_check_row;
I have read about a dozen articles here and I am still stumped with this issue.
I am building a dynamic select statement that will update a view on a monthly schedule.
set ansi_nulls on
go
set quoted_identifier on
go
alter procedure [dbo].[Proc_Name_SP]
as
begin
set nocount on
set quoted_identifier off
declare #dbname varchar(10), #schema_id int, #schema_name varchar(10),
#jacro varchar(10), #rec_cnt int, #tot_rec int
declare #SQL_Main nvarchar(max), #SQL_Final nvarchar(max),
#SQL_schema nvarchar(2000), #SQL_Union nvarchar(max)
declare iteration cursor global static for
-- Begin statement for cursor array
select distinct db, code
from linkedserver.db.schema.Directory
where current_stage = 'live'
order by db
-- End statement for cursor array
-- get total number of cursor iterations to know when to stop
-- "union" statements
select #tot_rec = count(*) from (select distinct db, code
from [linkedserver].db.schema.Directory
where current_stage = 'live') as cur
-- begin loop
open iteration
fetch first from iteration into #dbname, #jacro
while ##fetch_status=0
begin
-- the schema used is not consistent. Because of the linked server it was
-- necessary to get the Schema_ID from the sys.tables and then pull the
-- schema name from sys.schema
set #SQL_schema = 'select #sch_id = schema_id from [linkedserver].'+#dbname+'.sys.tables where name = ''Manuscript'''
execute sp_executesql #SQL_schema, N'#sch_id int OUTPUT', #sch_id = #schema_id output
--print #schema_id
set #SQL_schema ='select #sch_name = name from [linkedserver].'+#dbname+'.sys.schemas where schema_id = '+cast(#schema_id as varchar)+''
execute sp_executesql #SQL_schema, N'#sch_name nvarchar(10) OUTPUT', #sch_name = #schema_name output
--print #schema_name
--building Select statement
set #SQL_Main ='
select jcode.Code as BILLING_ACRO
,s.start_dt as BILLING_DATE
,cmpt_ms_nm as MANUSCRIPT
,isnull(jcode.billing_type, ''reviewed'') as Billing_type
from [linkedserver].'+#dbname+'.'+#schema_name+'.Manuscript as m
join [linkedserver].'+#dbname+'.'+#schema_name+'.Step as s on m.ms_id = s.ms_id and m.ms_rev_no = s.ms_rev_no
join (select j_id, Code, billing_type from [linkedserver].db.schema.Directory where db = '''+#dbname+''') as jcode on jcode.j_id = m.j_id
where jcode.Code = '''+#jacro+'''
and m.ms_rev_no = 0
and s.stage_id = 190
and isnull(cmpt_ms_nm, '''') <> ''''
and s.step_id = (select min(s2.step_id)
from [linkedserver].'+#dbname+'.'+#schema_name+'.Step as s2
where s2.stage_id = 190
and s2.ms_id = m.ms_id
and s2.ms_rev_no = m.ms_rev_no)
'
set #rec_cnt = isnull(#rec_cnt, 0) + 1
if #SQL_Union is null
begin
set #SQL_Union = #SQL_Main
end
else if #tot_rec <> #rec_cnt
begin
set #SQL_Union = #SQL_Union + ' union ' + #SQL_Main
end
else
begin
set #SQL_Union = #SQL_Union + #SQL_Main
end
--print #rec_cnt
fetch next from iteration into #dbname, #jacro --next database
end -- while ##FETCH_STATUS=0
close iteration
deallocate iteration
-- build new view
print len(#SQL_Union)
set #SQL_Final = '
ALTER VIEW [dbo].[View_Name_VW]
AS
'+#SQL_Union+'
'
execute sp_executesql #SQL_Final
--grab string variables to table for troubleshooting
insert into Output_SQL(SQL_Final, SQL_Final_Len, SQL_Union, SQL_Union_Len)
select #SQL_Final, LEN(#SQL_Final), #SQL_Union, LEN(#SQL_Union)
set nocount off
end
go
I have read that others have had problems with this type of truncation and I have tried multiple suggestions but in the end the I am getting capped at 68274 in this code with nvarchar(max). For troubleshooting, I am saving the results of the variables and the len of these variables to a table to eliminate the SSMS cap on the display of strings.
I have tried cast(#varible as nvarchar(max)) on the right side of the = sign. I have changed the data type lengths (as the select that is being built is not that large, it is just large after it has been union for each unique customer)
I am open to any suggestions as I have tried many variations of datatype declarations for these variables.
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.
In an SQL Server 2005 database, I have a stored procedure. I get some date in put them in a temp table. I'd like loop in this temp table and depending of the value of some fields change the value of others and make some check. I have to do this for each row.
How can I do this ?
thanks,
UPDATE1
BEGIN
SET NOCOUNT ON
--Create temp table
CREATE TABLE #MyTempTable(
id int IDENTITY(1, 1),
PriceMax int,
PriceMin int
)
-- Insert in temp table
INSERT INTO #tmpReconciliation (PriceMax, PriceMin)
SELECT PriceMax = PriceMaxProduct,
PriceMin = PriceMinProduct
FROM Products
DECLARE #RowNum int
SELECT #RowNum = Count(*) From #MyTempTable
WHILE #RowNum > 0
BEGIN
if(....)
PriceMin = 0
....
END
--Drop temp table
DROP TABLE #MyTempTable
END
I read MSDN documentation for WHILE loop and CURSOR.
For example, let's imagine your temp table is named Employee :
DECLARE #Emp_id int
DECLARE Employee_Cursor CURSOR FOR
SELECT EmployeeID
FROM Employee;
OPEN Employee_Cursor;
FETCH NEXT FROM Employee_Cursor INTO #Emp_id;
WHILE ##FETCH_STATUS = 0
BEGIN
-- Here your actions
PRINT #Emp_id
FETCH NEXT FROM Employee_Cursor INTO #Emp_id;
END;
CLOSE Employee_Cursor;
DEALLOCATE Employee_Cursor;
GO
Here I decided to print EmployeeId, but everything is possible.
Tell us what are your checks, and what your temp table looks like if you need more help.
Can't you just use a cursor and inside the cursor run an update statement??
Cursors: http://www.jackdonnell.com/articles/SQL_CURSOR.htm
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