I am executing below script.
declare #id int = 0
while(#id < 10)
begin
declare #tbl as table (id int)
insert into #tbl values(#id)
set #id = #id + 1
SELECT * FROM #tbl
end
I am getting result like below.
but this script should give only one row every time in temp table because temp table is declaring every time in while loop and I am inserting only one value in table.
I don't understand this behavior of temp table, please suggest.
According to Transact-SQL Variables:
The scope of a variable is the range of Transact-SQL statements that can reference the variable. The scope of a variable lasts from the point it is declared until the end of the batch or stored procedure in which it is declared.
Each DECLARE is only "read" once:
SQL Server parce the code and read all the DECLARE statements at compile time.
It then reserves memory for theses variables.
Code can then use them from the location of their respecting DECLARE until the end of the script.
If we look at your script, #tbl has already been reserved in memory when line 1 is executed but the variable can only be used once the script reach line 5. However it is not reserved again and again on each iteration of the loop.
Related
I have 2 tables (1 staging table and 1 main operational table).
Both tables have the same structure.
For my solution,
I am using DB2Copy in program to insert 10000 records into staging table (4 seconds)
From the staging table, will move the data into main table using stored procedure (10 seconds)
However, it will lock the main table when running stored procedure.
I am suspecting because of the BEGIN and END which cause the stored procedure to act like a transaction.
I do not want the table to be locked when running stored procedure. (any suggestion?) Prefer: Stored procedure insert record by record into main table without transaction behavior.
Below is my code:
CREATE PROCEDURE SP_NAME ( )
LANGUAGE SQL
NOT DETERMINISTIC
CALLED ON NULL INPUT
EXTERNAL ACTION
OLD SAVEPOINT LEVEL
MODIFIES SQL DATA
INHERIT SPECIAL REGISTERS
BEGIN
--DECLARE TEMP VARIABLES
BEGIN
DECLARE MYCURSOR CURSOR WITH RETURN TO CALLER FOR
--SELECT STAGING TABLE
DECLARE CONTINUE HANDLER FOR NOT FOUND SET AT_END = 1;
OPEN MYCURSOR;
-- FETCH MYCURSOR INTO TEMP VARIABLES
WHILE AT_END = 0 DO
-- INSERT MAIN TABLE
-- FETCH MYCURSOR INTO TEMP VARIABLES
END WHILE;
CLOSE MYCURSOR;
END;
END;
My Environment
I have a program "A" which is trying to insert 10k records into main table (A lot of indexes and high volume of data) which takes 10 minutes ++.
About main operational table
High number of read but minimum updates and inserts at front end.
At back end, another program will frequently insert record into this table.
Only 1 instance of the back end program is allowed to run at one time
When you create the procedure, make sure your commitment-control setting is *NONE (a.k.a. autocommit). This should not lock your whole table
Adding the example
CREATE PROCEDURE userS.SP_TEST (
IN col_DATA Varchar(10) )
LANGUAGE SQL
SPECIFIC userS.SP_TEST
NOT DETERMINISTIC
MODIFIES SQL DATA
SET OPTION COMMIT = *NONE
BEGIN INSERT INTO userS.TABLE1 VALUES(col_DATA);
END
This is a SQL learning exercise. I have a 'tbl' with a single integer field. There are no indices on this table. I have this function:
CREATE OR REPLACE FUNCTION rcs()
RETURNS VOID AS $$
DECLARE
c CURSOR FOR SELECT * FROM tbl ORDER BY sequence;
s INTEGER;
r RECORD;
BEGIN
s := 0;
OPEN c;
LOOP
FETCH c INTO r;
EXIT WHEN NOT FOUND;
RAISE NOTICE 'got it';
r.sequence = s;
s := s + 1;
RAISE NOTICE '%', r.sequence;
END LOOP;
CLOSE c;
END;
$$ language 'plpgsql'
This loads and runs cleanly and the RAISE statements suggest that the 'sequence' field gets updated to 0, 1 etc. in accordance with the ORDER BY.
However, when I SELECT the table afterwards, the pre-existing values (which happen to all be '6') did not change.
Is this something to do with transactions? I tried fiddling around with COMMIT, etc. to no avail.
This is a freshly installed Postgresql 9.4.4 running on a Linode with no hackery of config files or anything like that, from the 'psql' command line.
EDIT: maybe it's because 'r' isn't actually the DB table, it's some kind of temporary copy of it? If so, please clarify, hopefully what I'm trying to achieve here is obvious (and I know it may be a bit nonsensical, but surely it's possible without resorting to reading the set into Java, etc.)
The actual problem: your function does not contain an UPDATE statement so nothing gets written to disk. r.sequence = s; simply assigns a new value to a variable that is held in memory.
To fix this, you need something like:
UPDATE tbl
set sequence = s -- change the actual column in the table
WHERE current of c; -- for the current row of your cursor
If where current of doesn't work, you need to switch that to a "regular" where clause:
UPDATE tbl
set sequence = s
WHERE tbl.pk_column = r.pk_column; -- the current primary key value
But a much more efficient solution is to do this in a single statement:
update tbl
set sequence = tu.new_sequence
from (
select t.pk_column,
row_number() over (order by t.sequence) as new_sequence
from tbl t
) tu
where tbl.pk_column = tu.pk_column;
You need to replace the column name pk_column with the real primary key (or unique) column of your table.
I have a large query in Firebird (which I run using FlameRobin), using a parameter all over the place, but getting the query below to run will do:
SELECT * FROM customers WHERE customerid = 1234;
I want to define 1234 as a variable, say customerID, so that I can easily replace it with something else.
I've learned that I need to put this inside a EXECUTE BLOCK.
EXECUTE BLOCK
AS
DECLARE customerID INT = 1234;
BEGIN
SELECT * FROM customers WHERE customerid = :customerID
END
If of any importance, the error I am getting is Engine Message :
Dynamic SQL Error
SQL error code = -104
Unexpected end of command - line 3, column 26
The problem is that FlameRobin needs to know when a statement ends and the next statement starts. By default it uses the semicolon (;) for this. However an EXECUTE BLOCK is essentially a stored procedure that isn't stored in the database, so it contains PSQL code that also uses the semicolon as a statement separator.
The consequence of this is that you get syntax errors because FlameRobin is sending incomplete statements to the server (that is: it is sending a statement after each ; it encounters).
You need to instruct FlameRobin to use a different statement terminator using SET TERM. Other Firebird query tools (eg isql) require this as well, but it is not actually part of the syntax of Firebird itself!
So you need to execute your code as:
-- Instruct flamerobin to use # as the terminator
SET TERM #;
EXECUTE BLOCK
AS
DECLARE customerID INT = 1234;
BEGIN
SELECT * FROM customers WHERE customerid = :customerID;
END#
-- Restore terminator to ;
SET TERM ;#
However doing this will still result in an error, because this query is invalid for PSQL: A SELECT in a PSQL block requires an INTO clause to map the columns to variables. And to get the values out of the EXECUTE BLOCK returned to FlameRobin you also need to specify a RETURNS clause as described in the documentation of EXECUTE BLOCK:
-- Instruct flamerobin to use # as the terminator
SET TERM #;
EXECUTE BLOCK
RETURNS (col1 INTEGER, col2 VARCHAR(100))
AS
DECLARE customerID INT = 1234;
BEGIN
SELECT col1, col2 FROM customers WHERE customerid = :customerID INTO :col1, :col2;
SUSPEND;
END#
-- Restore terminator to ;
SET TERM ;#
As far as I know the SUSPEND is technically not required here, but Flamerobin will not fetch the returned row if it isn't included.
However the above will not work if the select produces multiple rows. For that you need to use FOR SELECT ... DO combined with a SUSPEND:
-- Instruct flamerobin to use # as the terminator
SET TERM #;
EXECUTE BLOCK
RETURNS (col1 INTEGER, col2 VARCHAR(100))
AS
DECLARE customerID INT = 1234;
BEGIN
FOR SELECT col1, col2 FROM customers WHERE customerid = :customerID INTO :col1, :col2
DO
SUSPEND;
END#
-- Restore terminator to ;
SET TERM ;#
The SUSPEND here returns the row and waits until the caller has fetched that row and then continues with the FOR loop. This way it will iterate over the results.
IMHO this too much effort for parameterization. You might want to consider simply not parametrizing when you use FlameRobin, or use a tool that supports asking for parameter values for the normal parameter placeholders of Firebird (but to be honest I am not sure if there are any).
Well you need a input and return value, ex:
EXECUTE BLOCK (p_customerID integer=?)
returns(col_1 type of column table.id_cv)
as
begin
for
select col_1 from table
where customerID=:p_customerID
into col_1 do
suspend;
end
Delphi as client side:
myquery1.ParamByName('p_customerID ').Value:=1234;
myquery1.Open()
if not myquery1.IsEmpty then
begin
// using returning values
if myquery1.FieldbyName('col_1').AsString='this' then DoThat();
end;
Use this way ever yout need input parameters and/or returning values;
I have a TSQL sproc that does three loops in order to find relevant data. If the first loop renders no results, then the second one normally does. I append another table that has multiple values that I can use later on.
So at most I should only have two tables returned in the dataset from the sproc.
The issue is that if the first loop is blank, I then end up with three data tables in my data set.
In my C# code, I can remove this empty table, but would rather not have it returned at all from the sproc.
Is there a way to remove the empty table from within the sproc, given the following:
EXEC (#sqlTop + #sqlBody + #sqlBottom)
SET #NumberOfResultsReturned = ##ROWCOUNT;
.
.
.
IF #NumberOfResultsReturned = 0
BEGIN
SET #searchLoopCount = #searchLoopCount + 1
END
ELSE
BEGIN
-- we have data, so no need to run again
BREAK
END
The process goes as follows: On the first loop there could be no results. Thus the rowcount will be zero because the EXEC executes a dynamically created SQL query. That's one table.
In the next iteration, results are returned, making that two data tables in the dataset output, plus my third one added on the end.
I didn't want to do a COUNT(*) then if > 0 then perform the query as I want to minimize the queries.
Thanks.
You can put the result for your SP in a table variable and then check if the table variable has any data in it.
Something like this with a SP named GetData that returns one integer column.
declare #T table(ID int)
declare #SQL varchar(25)
-- Create dynamic SQL
set #SQL = 'select 1'
-- Insert result from #SQL to #T
insert into #T
exec (#SQL)
-- Check for data
if not exists(select * from #T)
begin
-- No data continue loop
set #searchLoopCount = #searchLoopCount + 1
end
else
begin
-- Have data so wee need to query the data
select *
from #T
-- Terminate loop
break
end
Hi I was checking some store procedures of a product installed on my company, you know to see how other people solve problems and learn.
Among this I found this but I do not know what is the meaning of the # in the sql sp in the line select distinct objecttype from #CascadeCollect, any comments please?
This is the whole sp..
begin
-- get all the unique otcs collected in the temp table.
declare #EntityCode int
-- check if the entity requires special casing.
declare #DbCascadeMask int
-- special casing for calendar delete
exec p_DeleteCalendar
declare otccursor cursor for
select distinct objecttype from #CascadeCollect <------ here is the # ....
open otccursor
fetch otccursor into #EntityCode
while ##fetch_status = 0
begin
select #DbCascadeMask = DbCascadeMask
from EntityView as Entity
where Entity.ObjectTypeCode = #EntityCode
if #DbCascadeMask <> 0
begin
exec p_BulkDeleteGeneric #EntityCode
end
fetch otccursor into #EntityCode
end
CLOSE otccursor
DEALLOCATE otccursor
-- Return the count of entity instances that are still not deleted (because they
-- require platform bizlogic/special casing.
select count(*) as NotDeletedCount from #CascadeCollect where processed = 2
end
Thanks for any comments !!!
A single # as a prefix indicates a locally scoped temporary object. In this case it is clearly a table but you can also have #temp procedures as well.
It is only visible to the batch in which it is created (and any child batches) and dropped automatically when the batch exits.
So if that is the whole stored procedure then it is obviously expected to be run from another procedure that actually creates the temp table.
You can also have global temporary objects prefixed with ##.
a table prefixed with # is a local temporary table, it will be dropped once it is out of scope
create table #test(id int)
insert #test values (1)
select * from #test
If you run this from another connection select * from #test the table is not available since it is local