What do you think , does the Stored Procedure always return 1 ?
I am concerned about the if exists(..)
BEGIN
DECLARE #IsUserExisting bit
SET NOCOUNT ON
IF Exists
(
Select null FROM G_User WHERE
SamAccountName = #SamAccountName
AND NetBIOSDomainName = #NetBIOSDomainName
)
BEGIN
SET #IsUserExisting = 1
END
ELSE
BEGIN
SET #IsUserExisting = 0
END
Select #IsUserExisting
END
No, if the WHERE clause doesn't return anything IF Exists() returns false and consequently #IsUserExisting is set to 0.
Makis already answered your question, but i would like to suggest the following
You could simplify your code with:
declare #IsUserExisting bit;
set #IsUserExisting = (
select count(*) from G_User
where SamAccountName = #SamAccountName and
NetBIOSDomainName = #NetBIOSDomainName);
select #IsUserExisting;
I think the following is even shorter in your case.
select count(*) from G_User
where SamAccountName = #SamAccountName and
NetBIOSDomainName = #NetBIOSDomainName)
BEGIN
DECLARE #IsUserExisting bit
SET NOCOUNT ON
IF Exists
(
Select null FROM G_User WHERE
SamAccountName = #SamAccountName
AND NetBIOSDomainName = #NetBIOSDomainName
)
BEGIN
SET #IsUserExisting = 1
END
ELSE
BEGIN
SET #IsUserExisting = 0
END
Select #IsUserExisting
END
i tried using this but when true it sets #IsUserExisting = -1 !!
You should not use Select Count() if you are only testing to see if something exists. While it should return fairly quickly in this case, If EXISTS() will return true immediately when it finds a matching record. Select Count() will look at all records in order to give you a complete count, thus adding unnecessary overhead.
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;
This nested while loop is only successfully executing the lowest level loop. It refuses to add 1 to #wKey even though I tell it to SET #wKey = #wKey + 1.... What am I doing wrong here? Does SQL not allow nested loops?
DECLARE #fMinKey INT;
DECLARE #fMaxKey INT;
DECLARE #wMaxKey INT;
DECLARE #wKey INT;
DECLARE #wDate date;
DECLARE #fcStart DATE;
SET #fMinKey = (select min([fcKey]) from dbo.fact_Fc);
SET #fMaxKey = (select max([fcKey]) from dw.fact_Fc);
SET #wMaxKey = (select max([WellKey]) from dw.fact_Fc);
SET #wKey = 1;
SET #wDate =
(select min([fapDate]) from dbo.dim_W where [Key] = #wKey);
SET #fcStart =
(select min([Date]) from dw.fact_Fc where [wKey] = #wKey);
WHILE (#fMinKey <= #fMaxKey)
BEGIN
WHILE (#wKey <= #wMaxKey)
BEGIN
WHILE (#wDate < #fcStart)
BEGIN
INSERT INTO dw.fact_FcTemp2 ([wKey], [Date], [pAmount], [fcKey], [AddedDate])
VALUES (#wKey, #wDate, 0, #fMinKey, CURRENT_TIMESTAMP)
SET #wDate = dateadd(DAY, 1, #wDate)
END
SET #wKey = #wKey + 1
END
SET #fMinKey = #fMinKey + 1
END
The resulting table is only showing [wKey] = 1, but it should have rows for multiple different wKeys
Once when #wDate reach #fcStart looks like you never reset #wDate to initial state
So next loop run again
You need some how to reset #wDate for next loop
Also having 3 loops perhaps is miss of design, sql performances does not like loops at all.
Can you show us data structure and needed result maybe tehe is way to constuct sql whitout 3 nested loops
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.
Is there a way in to just run a query once to select into a variable, considering that the query might return nothing, then in that case the variable should be null.
Currently, I can't do a select into a variable directly, since if the query returns nothing, the PL/SQL would complain variable not getting set. I can only run the query twice, with the first one do the count and if the count is zero, set the variable to null, and if the count is 1, select into the variable.
So the code would be like:
v_column my_table.column%TYPE;
v_counter number;
select count(column) into v_counter from my_table where ...;
if (v_counter = 0) then
v_column := null;
elsif (v_counter = 1) then
select column into v_column from my_table where ...;
end if;
thanks.
Update:
The reason I didn't use exception is I still have some following logic after assigning the v_column, and I have to use goto in the exception section to jump back to the following code. I'm kind of hesitate of goto lines.
You can simply handle the NO_DATA_FOUND exception by setting your variable to NULL. This way, only one query is required.
v_column my_table.column%TYPE;
BEGIN
BEGIN
select column into v_column from my_table where ...;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_column := NULL;
END;
... use v_column here
END;
I know it's an old thread, but I still think it's worth to answer it.
select (
SELECT COLUMN FROM MY_TABLE WHERE ....
) into v_column
from dual;
Example of use:
declare v_column VARCHAR2(100);
begin
select (SELECT TABLE_NAME FROM ALL_TABLES WHERE TABLE_NAME = 'DOES NOT EXIST')
into v_column
from dual;
DBMS_OUTPUT.PUT_LINE('v_column=' || v_column);
end;
What about using MAX?
That way if no data is found the variable is set to NULL, otherwise the maximum value.
Since you expect either 0 or 1 value, MAX should be OK to use.
v_column my_table.column%TYPE;
select MAX(column) into v_column from my_table where ...;
Using an Cursor FOR LOOP Statement is my favourite way to do this.
It is safer than using an explicit cursor, because you don't need to remember to close it, so you can't "leak" cursors.
You don't need "into" variables, you don't need to "FETCH", you don't need to catch and handle "NO DATA FOUND" exceptions.
Try it, you'll never go back.
v_column my_table.column%TYPE;
v_column := null;
FOR rMyTable IN (SELECT COLUMN FROM MY_TABLE WHERE ....) LOOP
v_column := rMyTable.COLUMN;
EXIT; -- Exit the loop if you only want the first result.
END LOOP;
From all the answers above, Björn's answer seems to be the most elegant and short. I personally used this approach many times. MAX or MIN function will do the job equally well. Complete PL/SQL follows, just the where clause should be specified.
declare v_column my_table.column%TYPE;
begin
select MIN(column) into v_column from my_table where ...;
DBMS_OUTPUT.PUT_LINE('v_column=' || v_column);
end;
I would recommend using a cursor. A cursor fetch is always a single row (unless you use a bulk collection), and cursors do not automatically throw no_data_found or too_many_rows exceptions; although you may inspect the cursor attribute once opened to determine if you have a row and how many.
declare
v_column my_table.column%type;
l_count pls_integer;
cursor my_cursor is
select count(*) from my_table where ...;
begin
open my_cursor;
fetch my_cursor into l_count;
close my_cursor;
if l_count = 1 then
select whse_code into v_column from my_table where ...;
else
v_column := null;
end if;
end;
Or, even more simple:
declare
v_column my_table.column%type;
cursor my_cursor is
select column from my_table where ...;
begin
open my_cursor;
fetch my_cursor into v_column;
-- Optional IF .. THEN based on FOUND or NOTFOUND
-- Not really needed if v_column is not set
if my_cursor%notfound then
v_column := null;
end if;
close my_cursor;
end;
I use this syntax for flexibility and speed -
begin
--
with KLUJ as
( select 0 ROES from dual
union
select count(*) from MY_TABLE where rownum = 1
) select max(ROES) into has_rows from KLUJ;
--
end;
Dual returns 1 row, rownum adds 0 or 1 rows, and max() groups to exactly 1. This gives 0 for no rows in a table and 1 for any other number of rows.
I extend the where clause to count rows by condition, remove rownum to count rows meeting a condition, and increase rownum to count rows meeting the condition up to a limit.
COALESCE will always return the first non-null result. By doing this, you will get the count that you want or 0:
select coalesce(count(column) ,0) into v_counter from my_table where ...;
I want to dynamically use TOP or not sort of like this...
SELECT #SomeNumber CASE WHERE 0 THEN TOP 5 COLUMNNAME
ELSE COLUMNNAME
END
FROM TABLE
I hope to have understood your problem: you want to select the TOP 5 rows if you pass #SomeNumber = 0 else select all th etable rows
As a first straight implementation you can do something like that
declare #SomeNumber as int
set #SomeNumber = 5
-- set #SomeNumber = 1
SELECT TOP (SELECT #SomeNumber) COLUMNNAME FROM MYTABLE
you can change the parameter value in order to have how many rows you want
Otherwise i suggest you to implement a stored procedure (and maybe you already did that, otherwise you can follow the next steps in order to do it)
CREATE procedure [dbo].[TOPCLAUSE]
-- clause parameter
#SomeNumber as integer
AS
IF #SomeNumber = 0
BEGIN
SELECT TOP 5 COLUMNNAME FROM MYTABLE
END
ELSE
BEGIN
SELECT COLUMNNAME FROM MYTABLE
END
GO
Then you can call
exec [dbo].[TOPCLAUSE] 0
exec [dbo].[TOPCLAUSE] 1
I probably not answered your question but let me know if it helped you
I don't think you can.
You could either use dynamic SQL:
Declare #int int
set #int = 10
exec ('Select top ' + #int + ' * From Customers')
Or you could set rowcount
if (#someNumber != 0)
begin
set rowcount 5
end
select * From Customers
set rowcount 0
I've just used something like this:-
Declare #SQL nvarchar(max), #Params nvarchar(max)
set #Params = N''
Set #SQL = N'SELECT ' + Cast(#SomeNumber as varchar) + ' CASE WHERE 0 THEN TOP 5 COLUMNNAME
ELSE COLUMNNAME
END
FROM TABLE'
exec sp_executesql #SQL, #Params
Short answer is no, not the way you have it.
You can however use IF to test and run a different query:
IF (#SomeNumber = 0)
BEGIN
SELECT TOP 5 ColumnName FROM Table
END
ELSE
BEGIN
SELECT ColumnName FROM Table
END
Two options: conditional SQL or dynamic SQL.
(1) Conditional:
IF #SomeNumber = 0
SELECT TOP 5 COLUMNAME FROM TABLE
ELSE
SELECT COLUMNAME FROM TABLE
(2) Dynamic: build up the query in a varchar() and pass it to sp_execute
Another loophole: make use of subquery's with row_number function
DECLARE #DoTopJN AS bit
SET #DoTopJN = 0 -- or 1
SELECT X.Sequence
X.COLUMNA
--etc
FROM (SELECT ROW_NUMBER() OVER (ORDER BY Y.Code) AS Sequence
,Y.COLUMNA
,Y.COLUMNB
-- etc.
FROM Y) X
WHERE ((#DoTopJN = 0) OR (X.Sequence = 1))
I don't think this is possible because TOP is applied on not just a column but the whole row. You would have to create two different select statements and put them in a IF ELSE construct.
To correct SPE109's code:
DECLARE #SomeNumber INT = 0
DECLARE #SQL nvarchar(max), #Params nvarchar(max)
set #Params = N''
SELECT #SQL = N'SELECT ' + CASE WHEN #SomeNumber = 0 THEN '' ELSE 'TOP ' + CAST(#SomeNumber as varchar) END + ' COLUMNNAME FROM TABLE'
exec sp_executesql #SQL, #Params