How do I check if a particular element exists in a table - how can I return true or false?
I have a table that has
user_id
user_password
user_secretQ
Verbally, I want to do this: If a particular user_id exists in the user_id column, then return true -- otherwise return false.
There is no Boolean type in Oracle SQL. You will need to return a 1 or 0, or some such and act accordingly:
SELECT CASE WHEN MAX(user_id) IS NULL THEN 'NO' ELSE 'YES' END User_exists
FROM user_id_table
WHERE user_id = 'some_user';
In PL/SQL you can do this:
function user_exists (p_user_id users.user_id%type) return boolean
is
l_count integer;
begin
select count(*)
into l_count
from users
where user_id = p_user_id;
return (l_count > 0);
end;
This would then be used in calling PL/SQL like this:
if user_exists('john') then
dbms_output.put_Line('John exists');
end if;
NOTE: I used count(*) in the query in the knowledge that this will only return 1 or 0 in the case of a primary key search. If there could be more than one row then I would add "and rownum = 1" to the query to prevent unnecessarily counting many records just to find out if any exists:
function user_has_messages (p_user_id users.user_id%type) return boolean
is
l_count integer;
begin
select count(*)
into l_count
from messages
where user_id = p_user_id
AND ROWNUM = 1;
return (l_count > 0);
end;
Oracle RDBMS does not have boolean data type, you can only use boolean variables in PL/SQL.
If you simply want to return strings 'TRUE' and 'FALSE'
you can do this..
SELECT 'TRUE' FROM DUAL WHERE EXISTS (SELECT 'x' FROM table WHERE user_id = 'id')
UNION
SELECT 'FALSE' FROM DUAL WHERE NOT EXISTS (SELECT 'x' FROM table WHERE user_id = 'id')
I like #DCookie's query though.
select count(*) from table where userid = :userid and rownum <= 1); -- If exists then 1 else 0
Or you could do this:
select decode(max(USER_ID), null, 'FALSE', 'TRUE') BOOL_VAL
from USER_TABLE where USER_ID = [some USER_ID here]
Related
Here is the Query which I need to convert into the Procedure.
MERGE INTO table1 SAI
USING
<IF> :SITE_PROJECTS_ID: != null &&:SITE_PROJECTS_ID: != 0 <THEN>(SELECT * from table2)AX
ON (SAI.SITE_INFO_ID=AX.SITE_INFO_ID)
WHEN MATCHED THEN UPDATE SET
SAI.column1 = AX.column1,
SAI.column2 = AX.column2,
SAI.LAST_MODIFIED_BY = AX.LAST_MODIFIED_BY,
SAI.LAST_MODIFIED_DATE = SYSDATE
<ELSE>
DUAL ON (SAI.SITE_INFO_ID=:SITE_INFO_ID:)
WHEN MATCHED THEN UPDATE SET
<IF> :value1: != null && :value2: != '' <THEN> SAI.SITE_TRAKER_SITE_ID = :value3:, <ENDIF>
SAI.LAST_MODIFIED_DATE = sysdate
<ENDIF>"
Above merge statement needs to get converted into the below PL/SQL format:
DECLARE v_rowcount NUMBER DEFAULT 0;
BEGIN
FOR ax IN ( SELECT * FROM table2 )
LOOP
UPDATE table1 SET status = ax.status
WHERE project_id = ax.site_projects_id;
v_rowcount := SQL%rowcount;
IF ( v_rowcount = 0 )
THEN INSERT INTO table1 ( fuze_project_id, status )
VALUES ( ax.site_projects_id, ax.status ); v_rowcount := 0;
END IF;
END LOOP;
END;/
I am confused for updating two tables as when the condition met I need a different table and on else I am getting DUAL table, and appropriately updation takes place.
Please guide me about how to proceed and accomplish this task.
Just use an IF-ELSE statement and split the MERGE statement into two:
BEGIN
IF :SITE_PROJECTS_ID != 0 THEN
MERGE INTO table1 SAI
USING table2 AX
ON (SAI.SITE_INFO_ID=AX.SITE_INFO_ID)
WHEN MATCHED THEN
UPDATE
SET SAI.column1 = AX.column1,
SAI.column2 = AX.column2,
SAI.LAST_MODIFIED_BY = AX.LAST_MODIFIED_BY,
SAI.LAST_MODIFIED_DATE = SYSDATE;
ELSIF :value1 IS NOT NULL AND :value2 IS NOT NULL THEN
UPDATE table1
SET SITE_TRAKER_SITE_ID = :value3,
LAST_MODIFIED_DATE = sysdate;
END IF;
END;
/
Note: != NULL will never be true; you want IS NOT NULL. Also, in Oracle, '' is identical to NULL.
I have a store procedure that will
Update maximum 500 rows status from 0 to 1
Return those rows to program via cursor
Here is my store procedure code
PROCEDURE process_data_out (
o_rt_cursor OUT SYS_REFCURSOR
) IS
v_limit NUMBER;
l_data_ids VARCHAR2(32000);
BEGIN
v_limit := 500; -- limit 500
l_data_ids := '';
-- Create loop to get data
FOR i IN (
SELECT *
FROM
(
SELECT id FROM
TBL_DATA a
WHERE
a.created_at BETWEEN SYSDATE - 0.5 AND SYSDATE + 0.1
AND a.status = 0
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_1)
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_2 WHERE IS_DENY = 1)
ORDER BY
priority
)
WHERE
ROWNUM <= v_limit
) LOOP
BEGIN
-- Build string of ids like id1,id2,id3,
l_data_ids := l_data_ids
|| i.id
|| ',';
-- update row status to prevent future repeat
UPDATE TBL_DATA
SET
status = 1
WHERE
id = i.id;
END;
END LOOP;
COMMIT;
-- If string of ids length >0 open cursor to take data
IF ( length(l_data_ids) > 0 )
THEN
-- Cut last comma id1,id2,id3, --> id1,id2,id3
l_data_ids := substr(l_data_ids,1,length(l_data_ids) - 1);
-- open cursor
OPEN o_rt_cursor FOR
SELECT
id,
phone
FROM
TBL_DATA a
WHERE
a.id IN (
SELECT
to_number(column_value)
FROM
XMLTABLE ( l_data_ids )
);
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END process_data_out;
I want to optimize this performance and here is my question
Should I replace in by exists
Replace
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_1)
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_2 WHERE IS_DENY = 1)
by
AND NOT Exists (SELECT phone FROM TBL_BIG_TABLE_1 where TBL_BIG_TABLE_1.phone = a.phone)
AND NOT Exists (SELECT phone FROM TBL_BIG_TABLE_2 WHERE TBL_BIG_TABLE_2.phone = a.phone and IS_DENY = 1)
Is there a better way than
Save a string of ids like id1,id2,id3 after update row status
Open cursor by select from string of ids
I appreciate for any suggestion.
Thank for your concern
Row-by-row processing is always slower and you are also creating the string of ids, which again takes time so overall performance is going down.
You can use the collection DBMS_SQL.NUMBER_TABLE to store the updated ids from the UPDATE statement using the RETURNING clause and use it in the cursor query.
Also, I have changed your update statement so that it does not use NOT IN and uses the LEFT JOINS and ROW_NUMBER analytical function for increasing the performance as follows:
CREATE OR REPLACE PROCEDURE PROCESS_DATA_OUT (
O_RT_CURSOR OUT SYS_REFCURSOR
) IS
V_LIMIT NUMBER;
L_DATA_IDS DBMS_SQL.NUMBER_TABLE;
BEGIN
V_LIMIT := 500; -- limit 500
UPDATE TBL_DATA A
SET A.STATUS = 1
WHERE A.ID IN (
SELECT ID
FROM ( SELECT ID,
ROW_NUMBER() OVER(ORDER BY PRIORITY) AS RN
FROM TBL_DATA B
LEFT JOIN TBL_BIG_TABLE_1 T1 ON T1.PHONE = B.PHONE
LEFT JOIN TBL_BIG_TABLE_2 T2 ON T2.IS_DENY = 1 AND T2.PHONE = B.PHONE
WHERE B.CREATED_AT BETWEEN SYSDATE - 0.5 AND SYSDATE + 0.1
AND B.STATUS = 0
AND T1.PHONE IS NULL
AND T2.PHONE IS NULL)
WHERE RN <= V_LIMIT ) RETURNING ID BULK COLLECT INTO L_DATA_IDS;
OPEN O_RT_CURSOR FOR SELECT ID, PHONE
FROM TBL_DATA A
WHERE A.ID IN (SELECT COLUMN_VALUE FROM TABLE ( L_DATA_IDS ));
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END PROCESS_DATA_OUT;
/
I like to do construct a compare and swap operation so that it can be included in a batched transaction where if the cas statement fails, the rest of the statements will also fail.
This statement will update if time is 0 but doesn't throw an error if time is not 0:
UPDATE account
SET time = '1'
WHERE id = (
SELECT id FROM account WHERE id = 'id-0' and time = '0' LIMIT 1
) RETURNING id
I want to add something like this
IF NO DATA THEN
RAISE EXCEPTION 'cas ';
END IF;
but not sure how to do it.
If you cannot use a plpgsql block for whatever reason, you can force a division by zero:
UPDATE account
SET time = '1'
WHERE id = (
SELECT id FROM account WHERE id = 'id-0' and time = '0' LIMIT 1
)
AND 1/(select count(*) from account where id = 'id-0' and time = '0') is not null
RETURNING id;
This is like stopping the bicycle I'm riding by jamming a stick in the front wheel spokes.
This works for me:
DO $$
DECLARE
v_id TEXT;
BEGIN
UPDATE "account"
SET (id, name) = ('t1', 'world')
WHERE id = ( SELECT id FROM "account" WHERE "id" = 't1' AND "name" = 'hello' LIMIT 1)
RETURNING id INTO v_id;
IF count(v_id) = 0
THEN RAISE EXCEPTION 'Cas Error for (account, t1)';
END IF;
END $$
I am using a stored procedure to return the type of student that is enrolled at my college. Pushing their ID through should return their first name and last name in a new column that is going to be made(Ex: commuter, employee, resident). I keep getting an error:
ERROR: syntax error at or near "if"
LINE 8: if exists (select count(commuterid) > 0 from commuter wh...).
Any tips or ideas?
create or replace function roleAtMarist(int, REFCURSOR) returns refcursor as
$$
declare
identifier int := $1;
resultset refcursor := $2;
begin
open resultset for
if exists (select count(commuterid) > 0 from commuter where commuterid = identifier) then
select fname, lname, "Commuter" as Role
from people
where peopleid = identifier;
end if;
if exists (select count(employeeid) > 0 from employee where emplpoyeeid = identifier) then
select fname, lname, "Employee" as Role
from people
where peopleid = identifier;
end if;
if exists (select count(residentid) > 0 from studentpark where residentid = identifier) then
select fname, lname, "Resident" as Role
from people
where peopleid = identifier;
end if;
return resultset;
end;
$$
language plpgsql;
select roleAtMarist(12, 'resultset') ;
fetch all from results ;
This is backwards in multiple ways. A cursor would use valid SQL and no plpgsql commands. But you don't need a cursor nor plpgsql to begin with. A simple SQL function should do:
CREATE OR REPLACE FUNCTION role_at_marist(_identifier int)
RETURNS TABLE (fname text, lname text, "role" text) AS
$func$
SELECT p.fname, p.lname, text 'Commuter'
FROM people
WHERE p.peopleid = $1
AND EXISTS (SELECT 1 FROM commuter c WHERE c.commuterid = p.peopleid)
UNION ALL
SELECT p.fname, p.lname, 'Employee'
FROM people
WHERE p.peopleid = $1
AND EXISTS (SELECT 1 FROM employee e WHERE e.emplpoyeeid = p.peopleid)
UNION ALL
SELECT p.fname, p.lname, 'Resident'
FROM people
WHERE p.peopleid = $1
AND EXISTS (SELECT 1 FROM studentpark s WHERE s.residentid = p.peopleid)
$func$ LANGUAGE sql;
Call:
SELECT * FROM role_at_marist(12);
Set-returning functions can be used just like tables in the FROM list.
String literals are enclosed in single quotes!
How do I set an empty set or null value to a default value like 1?
So far, I have this statement, but in case I get null values i want to handle that:
select case when count(*)=0
then 0
else 1
end OUTPUT
from TESTTBL
where timestamp = to_char(sysdate-1, 'yyyymmdd')||'0000';
Do you mean to check for Null value and set as some default, if so
select nvl(column_name,'DEFAULT') from TESTBL where timestamp = to_char(sysdate-1, 'yyyymmdd')||'0000';
SELECT CASE WHEN EXISTS
( SELECT *
FROM TESTTBL
WHERE timestamp = to_char(sysdate-1, 'yyyymmdd') || '0000'
)
THEN 1
ELSE 0
END AS OUTPUT
FROM dual
EDIT
Added FROM dual as Oracle does not allow SELECT without FROM clause.
Here you go
SELECT DECODE(count(*),0,0,
1) OUTPUT
FROM TESTTBL
WHERE TIMESTAMP = TO_CHAR(SYSDATE-1, 'yyyymmdd')||'0000';
Use Decode like
SELECT supplier_name,
decode(supplier_id, 10000, 'Google',
10001, 'Microsoft'
'Sony') result
FROM suppliers;
equivalent to
IF supplier_id = 10000 THEN
result := 'Google';
ELSIF supplier_id = 10001 THEN
result := 'Microsoft';
ELSE
result := 'Sony';
END IF;
Or Use coalesce
SELECT coalesce( address1, address2) result
FROM suppliers;
which is equivalent to
IF address1 is not null THEN
result := address1;
ELSIF address2 is not null THEN
result := address2;
ELSE
result := null;
END IF;