Execute same query multiple times with different parameter postgressql - sql

I don't know how to easily explain what i need to do but i'll try.
Lets say i have a user table with 5 users.
id name
1 Steve
2 Pat
3 Robin
4 Carl
5 Sarah
instead of just do a select * from users i need to do this different and more difficult.
I need to build a query that for each row in users table runs a query with a parameter (name) and then gives me the same output as select * from users
I know it's sounds wierd but this is what i actually needs to do..
So what i want to happen is this:
I need to loop thru the users table to find out how many rows there is. (5)
-That's the amount of times I want to execute the query.
For each execution of the Query i need to change name in the where clause. First execution = Steve, second = Pat and so on.
In the end I want just one output with everything together so I need to union the result.
If i would this manually it would look like this:
Select id, name from users where name = 'Steve'
union all
Select id, name from users where name = 'Pat'
union all
Select id, name from users where name = 'Robin'
union all
Select id, name from users where name = 'Carl'
union all
Select id, name from users where name = 'Sarah'
In my real case i need separate queries so a in ('Steve', 'Pat') or a solution like that won't work.
I hope you understand what im looking for, and if you have any question please ask. Im using postgres v.10

This should work as you intend to.
DO $$
DECLARE
var_req TEXT;
rec_key record;
cur_key CURSOR FOR SELECT distinct name from users;
BEGIN
open cur_key;
loop
fetch cur_key into rec_key;
EXIT WHEN NOT FOUND;
var_req := '
insert into your_output_table
select id, name from users
where name = '''||rec_key.name||'''
;
';
execute var_req;
end loop;
close cur_key;
END $$;

Related

SQL*Plus using a variable for a subselect (keeping spool working)

I got the following script that does actually the following, it uses an email-address to
search for that user
search for messages from that user
search for Entries of that user
puts the findings to a file
currently that is working but now I would like to do the following:
Replace the sub select "SELECT ID FROM DBSCHEMA.USERS WHERE EMAIL ='&SEARCH'" with a kind of variable to not put (and execute) this statement multiple times in the script.
But what ever I tried did not work. (Started using DECLARE block - than the spool was not working, end everything else just broke the script)
I'm using a oracle database - but i would like to stick to generic commands if possible.
DEFINE SEARCH='search#example.org';
spool OUTPUTFILE.out
prompt Start Searching by mail '&SEARCH'
SELECT
'ID;NAME;LAST_LOGIN;EMAIL_ADDRESS;FIRST_NAME;LAST_NAME;BLABLA;LANGUAGE_ID;'
FROM
DUAL;
SELECT
ID||';'||NAME||';'||LAST_LOGIN||';'||EMAIL||';'||FIRST_NAME||';'||LAST_NAME||';'||BLABLA||';'||LANGUAGE_ID||';'
FROM
DBSCHEMA.USERS
WHERE
EMAIL ='&SEARCH';
prompt Feedback messages
prompt ID;SUBJECT;MESSAGE;TIMESTAMP;
SELECT
ID||';'||SUBJECT||';'||MESSAGE||';'||TIMESTAMP||';'
FROM
DBSCHEMA.SITE_FEEDBACK
WHERE
USER_ID = (SELECT ID FROM DBSCHEMA.USERS WHERE EMAIL ='&SEARCH');
prompt Other Entries
prompt ID;TITLE;BLABLA;TIMESTAMP;
SELECT
ID||';'||TITLE||';'||BLABLA||';'||TIMESTAMP||';'
FROM
DBSCHEMA.SITE_ENTRY
WHERE
USER_ID = (SELECT ID FROM DBSCHEMA.USERS WHERE EMAIL ='&SEARCH');
spool off;
How does search get populated? Also, what version of SQL*Plus do you have?
In SQL*Plus you could declare a variable and populate it in PL/SQL, like this (I've used the HR demo schema):
define search='VJONES';
var user_id number
begin
select employee_id into :user_id from employees where email = '&search';
exception
when no_data_found then
raise_application_error(-20001, 'No user found with email ''&search''.');
when too_many_rows then
raise_application_error(-20002, 'Multiple users found with email ''&search''.');
end;
/
select phone_number from employees where employee_id = :user_id;
PHONE_NUMBER
--------------------
650.501.4876
(You don't strictly need the exception handler, but it will improve handling of invalid or duplicated data.)
You could also stick with define variables:
column user_id new_value user_id
select employee_id as user_id from employees where email = '&search';
USER_ID
----------
195
1 row selected.
Now you have defined and populated &user_id and you can use it in queries:
select phone_number from employees where employee_id = &user_id;
PHONE_NUMBER
--------------------
650.501.4876
I don't understand the parts of your question about things not working or using generic commands. If you could clarify those I might be able to add to my answer.

Passing Multiple Values into an API/Getting Multiple Values

Here's the issue that I'm working through today. We have an ERP system in place and I need to set up an event to email Purchase Order Authorizers (POA) whenever they have a Purchase Order ready for approval. This is pretty easy to set up for purchase orders where we have a single authorizer. However, some purchase orders need to be reviewed by a group of authorizers, specifically our QA group, before they can get approved.
I can get a list of authorizers for our QA group using the following:
select authorize_id from purch_authorize_group_line where authorize_group_id = '30-QA-NUC';
This query returns 50 rows of data.
I can also get the USERID associated with an Authorize ID using the following:
SELECT purchase_authorizer_api.get_userid('30','104351') FROM DUAL;
What I can't figure out is how to pass all values from the first query into the second one. This query:
SELECT purchase_authorizer_api.get_userid('30',(select authorize_id from purch_authorize_group_line where authorize_group_id = '30-QA-NUC')) FROM DUAL;
returns the error "ORA-01427: single-row subquery returns more than one row."
So, what I'm wondering, is whether there's a way for me to pass all 50 values from the first query into the second query and get USERIDs for all 50 users. If those USERIDs then I can get a notification email out to the QA group when they have a PO ready to approve.
Maybe you're looking for this.
SELECT purchase_authorizer_api.get_userid('30', authorize_id)
FROM purch_authorize_group_line
WHERE authorize_group_id = '30-QA-NUC';
This should help -
DECLARE
V_VARIABLE VARCHAR2(1024);
BEGIN
FOR REC IN (SELECT AUTHORIZE_ID AS VAL FROM PURCH_AUTHORIZE_GROUP_LINE WHERE AUTHORIZE_GROUP_ID = '30-QA-NUC')
LOOP
SELECT PURCHASE_AUTHORIZER_API.GET_USERID('30',R.VAL) INTO V_VARIABLE FROM DUAL;
..
..
../* You code processing logic */
END LOOP;
END;
/

Create view with except condition

Example i want to create a view name TEST
which i already created a view before that
View name : customer
Inside customer View:
//CUSTOMER
NAME ID ADDRESS AGE SEX TELNO EMAIL
-----------------------------------------------------------------------
CHRIS 1 12321312 21 F 646885 ascs#gmail
JOHN 2 SADASDSA 23 M 5452131 asd#gmail
MAY 3 LKJLKJLKJ 32 F 645643 cxz#gmail
So i want to create a view name TEST that will store all column inside CUSTOMER but EXCEPT TELNO EMAIL.
so i used this query:
CREATE VIEW TEST AS
SELECT * FROM CUSTOMER
EXCEPT
SELECT TELNO,EMAIL FROM CUSTOMER;
But i fail to work, got errors come out. SQL command not properly end and point to EXCEPT, what's wrong?
You have to list all the columns that you want explicitly:
CREATE VIEW TEST AS
SELECT NAME, ID, ADDRESS, AGE, SEX
FROM CUSTOMER;
There is no way to exclude some columns from a * list.
EXCEPT is an operator in SQL Server. The equivalent in Oracle is MINUS. However, this works at the row level, not at the column level.
If you want to get all the columns in the table, except for those two, you can use all_tab_columns:
select column_name
from all_tab_columns
where lower(table_name) = 'customer' and
lower(column_name) not in ('telno', 'email');
You can then paste them into the select clause.

SQL query to create a single record view of multiple records - Dynamic Stored Procedure?

Ok, I think there must be an easier way to do this, I would like to do it dynamically instead of hand writing every line. I am more familiar with MySQL than SQL Server 2008, but I'm not sure how I would do this in MySQL either.
I have 3 tables.
Table 1: USER id | email | password
Table 2: METADATA id | name (list of fields I need to know about user)
Table 3: USER_META id | uid | name | value (where I store the user meta data)
I do not hard code the METADATA because it changes for each instance of this application. but in this example, lets just say the meta data is "eyes", "phone" , "city" (there are actually many more, there might be 20 this week and 40 next week)
So the way it works is when a user registers, I build the registration form using the METADATA table to build the form. It creates a form with "eyes" , "phone" and "city".
When I save the form, I save a single record into the USER_META table for each data item.
So when registering, I get 3 inserts:
insert into USER_META(uid,name,value) values (5,"eyes","brown")
insert into USER_META(uid,name,value) values (5,"phone","555-1212")
insert into USER_META(uid,name,value) values (5,"city","San Francisco")
Now, I want to write a Stored Procedure that will return a record like this"
EXECUTE Get_Meta 5
returns:
uid | email | eyes | phone | city
5 x#x.com brown 555-1212 San Francisco;
I could write a long hard coded select statement like:
DECLARE #UID int;
SELECT id, email,
(select value from USER_META
where uid = #UID and name = 'EYES') as EYES,
(select value from USER_META
where uid = #UID and name = 'PHONE') as PHONE,
(select value from USER_META
where uid = #UID and name = 'CITY') as CITY,
FROM USER
where id = #UID;
But that kind of defeats the whole purpose, and I have to write it again every week whenever the metadata requirements change (eg when we launch another instance).
I would like to have something like this: (forgive me I am not very experienced with Advanced SQL, so maybe this is easy and I just don't know how to do it?) I will write it in basic code terms to try and explain my thinking:
DECLARE #UID int;
DECLARE #META_NAMES array;
#META_NAMES = (select NAME from METADATA);
SELECT id, email,
for each (#META_NAMES as $THIS_NAME) {
(select value from USER_META
where uid = #UID and name = '$THIS_NAME') as $THIS_NAME,
}
FROM USER
where id = #UID;
Is there a way to write this in SQL Server 2008?
You would USE FOR XML_PATH in SQL Server. A workaround for GROUP_CONCAT
SELECT
U.UID,
U.EMAIL,
stuff(
(
select cast(',' as varchar(max)) + name + '=' + Value
from USER_META
WHERE UID = U.UID
for xml path('')
), 1, 1, '') AS M
FROM
USERS U
SQL Fiddle

Getting original and modified content from a table with an audit trail

I came across the following table structure and I need to perform a certain type of query upon it.
id
first_name
last_name
address
email
audit_parent_id
audit_entry_type
audit_change_date
The last three fields are for the audit trail. There is a convention that says: all original entries have the value "0" for "audit_parent_id" and the value "master" for "audit_entry_type". All the modified entries have the value of their parent id for audit_parent_id" and the value "modified" for the "audit_entry_type".
Now what I want to do is to be able to get the original value and the modified value for a field and I want to make this with less queries possible.
Any ideas? Thank you.
Assuming a simple case, when you want to get the latest adress value change for the record with id 50, this query fits your needs.
select
p.id,
p.adress as original_address,
(select p1.adress from persons p1 where p1.audit_parent_id = p.id order by audit_change_date desc limit 1) as latest_address
from
persons p -- Assuming it's the table name
where
p.id = 50
But this assumes that, even if the address value doesn't change between one audit to the other, it remains the same in the field.
Here's another example, showing all persons that had an address change:
select
p.id,
p.adress as original_address,
(select p1.adress from persons p1 where p1.audit_parent_id = p.id order by audit_change_date desc limit 1) as latest_address
from
persons p -- Assuming it's the table name
where
p.audit_parent_id = 0
and
p.adress not like (select p1.adress from persons p1 where p1.audit_parent_id = p.id order by audit_change_date desc limit 1)
This can be solved with pure SQL in modern Postgres using WITH RECURSIVE.
For PostgreSQL 8.3, this plpgsql function does the job while it is also a decent solution for modern PostgreSQL. You want to ..
get the original value and the modified value for a field
The demo picks first_name as filed:
CREATE OR REPLACE FUNCTION f_get_org_val(integer
, OUT first_name_curr text
, OUT first_name_org text) AS
$func$
DECLARE
_parent_id int;
BEGIN
SELECT INTO first_name_curr, first_name_org, _parent_id
first_name, first_name, audit_parent_id
FROM tbl
WHERE id = $1;
WHILE _parent_id <> 0
LOOP
SELECT INTO first_name_org, _parent_id
first_name, audit_parent_id
FROM tbl
WHERE id = _parent_id;
END LOOP;
END
$func$ LANGUAGE plpgsql;
COMMENT ON FUNCTION f_get_org_val(int) IS 'Get current and original values for id.
$1 .. id';
Call:
SELECT * FROM f_get_org_val(123);
This assumes that all trees have a root node with parent_id = 0. No circular references, or you will end up with an endless loop. You might want to add a counter and exit the loop after x iterations.