This instruction works:
SELECT INTO unsolvedNodes array_agg(DISTINCT idDestination)
FROM road
WHERE idOrigin = ANY(solvedNodes)
AND NOT (idDestination = ANY(solvedNodes));
But I would like to use something this way:
SELECT INTO unsolvedNodes array_agg(DISTINCT idDestination), lengths array_agg(length)
FROM road
WHERE idOrigin = ANY(solvedNodes)
AND NOT (idDestination = ANY(solvedNodes));
How to use only one SELECT INTO instruction to set multiple variables?
In PL/pgSQL you can SELECT INTO as many variables at once as you like directly. You just had the syntax backwards:
SELECT INTO unsolvedNodes, lengths
array_agg(DISTINCT idDestination), array_agg(length)
FROM road
WHERE idOrigin = ANY(solvedNodes)
AND NOT (idDestination = ANY(solvedNodes));
You have the keyword INTO followed by a list of target variables, and you have a corresponding SELECT list. The target of the INTO clause can be (quoting the manual here):
...a record variable, a row variable, or a comma-separated list of
simple variables and record/row fields.
Also:
The INTO clause can appear almost anywhere in the SQL command.
Customarily it is written either just before or just after the list of
select_expressions in a SELECT command, or at the end of the command
for other command types. It is recommended that you follow this
convention in case the PL/pgSQL parser becomes stricter in future versions.
This is not to be confused with SELECT INTO in the SQL dialect of Postgres - which should not be used any more. It goes against standard SQL and will eventually be removed, most likely. The manual actively discourages its continued use:
It is best to use CREATE TABLE AS for this purpose in new code.
Yes,
SELECT name,family INTO cName, cFamily FROM "CommonUsersModel";
OR
SELECT INTO cName, cFamily name,family FROM "CommonUsersModel"
DO
$$
DECLARE var_one UUID;
var_two UUID;
var_three UUID;
var_four UUID;
var_five UUID;
BEGIN
Select INTO var_one,var_two,var_three,var_four,var_five
h.foocol1,
h.foocol2,
h.foocol3,
h.foocol4,
h.foocol5
from footable h
where h.fooid = 2;
END;
$$;
This is how you can set multiple variables using SELECT INTO
Thanks !
Related
I am currently writing a SQL query which first creates a lot of temporary tables using the WITH operator along with SELECT statements and then joins all of the temporary statements at the end.
All of my SELECT statements that create temporary tables depend on certain filters... so my query looks something liek
WITH
table_1 as (
SELECT product_id
avg(price)
FROM daily_sales
WHERE product_category = 1
AND sell_date BETWEEN TO_DATE('2012/01/07','YYYY/DD/MM') AND TO_DATE('2012/30/09','YYYY/DD/MM')
GROUP BY ds.product_id
),
table_2 as (....
),
SELECT FROM table_1 JOIN table_2....
I would like to run this query for ranges of 'sell_date' (a date, or a string) and different values of 'product_category' (an integer value).
Currently, I am replacing these manually but I am wondering if I can just declare replace these hard-coded values with variables, which I set at the top of my query.
I understand that this might have been asked before - but I am confused since there are multiple solutions that depend on the exact version of SQL that you are using and the types of variables that you are declaring.
In this case, I am looking for a solution that works in Oracle SQL, and where I can specify the type variable.
It depends how you're running your query.
If you're using an interactive client like SQL*Plus or TOAD you should use substitution variables:
WITH
table_1 as (
SELECT product_id
avg(price)
FROM daily_sales
WHERE product_category = &product_cat
AND sell_date BETWEEN TO_DATE('&start_date','YYYY/DD/MM') AND TO_DATE('&end_date','YYYY/DD/MM')
GROUP BY ds.product_id
),
You will be prompted to supply values for these variables each time you run the query. If you want to use the same values in multiple places then declare all the occurrences of a variable with a double ampersand - &&product_category - and then you only be prompted for it once. The SQL*Plus documentation has additional information: find out more.
If you're going to run the queries in a stored procedure then define the values as parameters ...
procedure process_sales_details
( i_product_category in number
, i_start_date in date
, i_end_date in date )
... which you reference in your query (wherever you declare it) ...
WITH
table_1 as (
SELECT product_id
avg(price)
FROM daily_sales
WHERE product_category = i_product_cat
AND sell_date BETWEEN i_start_date AND i_end_date
GROUP BY ds.product_id
),
Further to APC's answer, in SQL*Plus or SQL Developer you can also declare variables that you can assign values to in an anonymous PL/SQL block and then reference as bind variables in your plain SQL query:
variable v_product_cat number;
variable v_start_date varchar2(10);
variable v_end_date varchar2(10);
begin
:v_product_cat := 1;
:v_start_date := '2012/01/07';
:v_end_date := '2012/30/09';
end;
/
WITH table_1 as (
SELECT product_id
avg(price)
from daily_sales
where product_category = :v_product_cat
AND sell_date BETWEEN TO_DATE(:v_start_date,'YYYY/DD/MM')
AND TO_DATE(:v_end_date,'YYYY/DD/MM')
group by ds.product_id
)
...
Note the : before the variable name denoting a bind variable, and that the strings are not enclosed in quotes with this form. Unfortunately you can't declare a date variable, which would make this even neater.
And if you use substitution variables you can define them at the start so you aren't prompted; in this case you don't need to use the && notation either:
define v_product_cat=1
define v_start_date=2012/01/07
define v_end_date=2012/30/09
...
where product_category = &v_product_cat
and sell_date between to_date('&v_start_date','YYYY/DD/MM')
AND TO_DATE('&v_end_date','YYYY/DD/MM')
...
... which is covered in the documentation APC linked to.
You can add one or more common table expressions to encapsulate these:
with
cte_sell_dates as (
select date '2012-07-01' from_date,
date '2012-09-30' to_date
from dual),
cte_products as (
select 1 id from dual union all
select 28 id from dual),
... as (
select ...
from ...
where sell_date between (select from_date from cte_sell_dates) and
(select to_date from cte_sell_dates) and
product_id in (select id from cte_products )
...
... or use joins directly, instead of subqueries, of course.
Go for an Anonymous PL/sql Block and use a for loop where you can loop through all the different values.
Following is the Structure of pl/sql block:
DECLARE
<constant name> CONSTANT <data type> := <value>;
<constant name> CONSTANT <data type> DEFAULT <value>;
BEGIN
<valid statement>;
EXCEPTION
<exception handler>;
END;
Also you can go for a parametrized cursor where you can pass your values.
i have worked previously with SQL Server and was able to learn how to create temporary variables and use them in SQL
i used to write something like this:
declare #Student nvarchar(255)
select #Student = studentname from sometable where somecondition = 1
declare #teacher nvarchar(255)
select #teacher = teachername from sometable2 where somecondition >2
and then
select #student, #teacher, other columns from sometable where some condition
i want to do the same thing in ORACLE Database.
Please Help!
If you want to do this in SQL*Plus without using PL/SQL, you can use substitution variables:
column studentname new_value l_studentname
select studentname from sometable where somecondition = 1;
column teachername new_value l_teachername
select teachername from sometable2 where somecondition >2;
select '&l_studentname', '&l_teachername,' other columns
from sometable where somecondition;
The new_value clause in the column directive automatically assigns the value from any following select to a local variable, which I've prepended with l_ but you can call anything. You then reference that local variable in future queries with the & variable substitution syntax.
You can use them pretty much anywhere you'd normally have a value. e.g. in the where clause. Note that text values have to be enclosed in quotes, hence '&l_studentname'; without the quotes the value would be interpreted as a column name in this case, which wouldn't work.
You can declare a variable say
SOME_VAR VARCHAR2(255);
Then use it in your query directly
SELECT DISTINCT YT.TEACHER_NAME
INTO SOME_VAR
FROM YOUR_TABLE YT
WHERE YT.TEACHER_ID = 1;
Then you are free to use this variable, SOME_VAR, for further use
Of course, this will not work in a simple SQL statement, but in case you use it in a programming block, like a procedure.
Hope it helps
I have the following MySQL routine:
DELIMITER $$
CREATE DEFINER=`root`#`%` PROCEDURE `getGroupOrders`(grp INT,
ord CHAR(20),
srt CHAR(4),
page INT,
count INT)
BEGIN
SELECT *
FROM `dbre`.`order_info`
WHERE username IN (SELECT `dbre`.`users`.`username`
FROM `dbre`.`users`
WHERE `dbre`.`users`.`id_group` = grp)
ORDER BY ord srt LIMIT page,count;
END
As you can see, I want to pass the ordering column and sorting as a parameters, however I get a syntax error is there a way to do this or do I have to make similar routines for each type of ordering?
I don't think this is possible in the way you try it.
You cannot use a variable to define the ORDER BY column an direction.
The only workaround I can think of is to create a prepared statement from a dynamically created string (where you can use the variables to specify the order by details) and then execute that prepared statement.
Here is an example of such a dynamic statement:
http://forums.mysql.com/read.php?98,393613,393642#msg-393642
how can i write the store procedure for searching particular string in a column of table, for given set of strings (CSV string).
like : select * from xxx where tags like ('oscar','rahman','slumdog')
how can i write the procedure for that combination of tags.
To create a comma seperated string...
You could then apply this list to Oded example to create the LIKE parts of the WHERE cluase on the fly.
DECLARE #pos int, #curruntLocation char(20), #input varchar(2048)
SELECT #pos=0
SELECT #input = 'oscar,rahman,slumdog'
SELECT #input = #input + ','
CREATE TABLE #tempTable (temp varchar(100) )
WHILE CHARINDEX(',',#input) > 0
BEGIN
SELECT #pos=CHARINDEX(',',#input)
SELECT #curruntLocation = RTRIM(LTRIM(SUBSTRING(#input,1,#pos-1)))
INSERT INTO #tempTable (temp) VALUES (#curruntLocation)
SELECT #input=SUBSTRING(#input,#pos+1,2048)
END
SELECT * FROM #tempTable
DR0P TABLE #tempTable
First off, the use of like for exact matches is sub-optimal. Might as well use =, and if doing so, you can use the IN syntax:
select * from xxx
where tags IN ('oscar', 'rahman', 'slumdog')
I am guessing you are not looking for an exact match, but for any record where the tags field contains all of the tags.
This would be something like this:
select * from xxx
where tags like '%oscar%'
and tags like '%rahman%'
and tags like '%slumdog%'
This would be not be very fast or performant though.
Think about moving this kind of logic into your application, where it is faster and easier to do.
Edit:
Following the comments - there are lots of examples on how to parse delimited strings out there. You can put these in a table and use dynamic sql to generate your query.
But, this will have bad performance and SQL Server will not be able to cache query plans for this kind of thing. As I said above - think about moving this kind of logic to application level.
In MS SQL Server, I create my scripts to use customizable variables:
DECLARE #somevariable int
SELECT #somevariable = -1
INSERT INTO foo VALUES ( #somevariable )
I'll then change the value of #somevariable at runtime, depending on the value that I want in the particular situation. Since it's at the top of the script it's easy to see and remember.
How do I do the same with the PostgreSQL client psql?
Postgres variables are created through the \set command, for example ...
\set myvariable value
... and can then be substituted, for example, as ...
SELECT * FROM :myvariable.table1;
... or ...
SELECT * FROM table1 WHERE :myvariable IS NULL;
edit: As of psql 9.1, variables can be expanded in quotes as in:
\set myvariable value
SELECT * FROM table1 WHERE column1 = :'myvariable';
In older versions of the psql client:
... If you want to use the variable as the value in a conditional string query, such as ...
SELECT * FROM table1 WHERE column1 = ':myvariable';
... then you need to include the quotes in the variable itself as the above will not work. Instead define your variable as such ...
\set myvariable 'value'
However, if, like me, you ran into a situation in which you wanted to make a string from an existing variable, I found the trick to be this ...
\set quoted_myvariable '\'' :myvariable '\''
Now you have both a quoted and unquoted variable of the same string! And you can do something like this ....
INSERT INTO :myvariable.table1 SELECT * FROM table2 WHERE column1 = :quoted_myvariable;
One final word on PSQL variables:
They don't expand if you enclose them in single quotes in the SQL statement.
Thus this doesn't work:
SELECT * FROM foo WHERE bar = ':myvariable'
To expand to a string literal in a SQL statement, you have to include the quotes in the variable set. However, the variable value already has to be enclosed in quotes, which means that you need a second set of quotes, and the inner set has to be escaped. Thus you need:
\set myvariable '\'somestring\''
SELECT * FROM foo WHERE bar = :myvariable
EDIT: starting with PostgreSQL 9.1, you may write instead:
\set myvariable somestring
SELECT * FROM foo WHERE bar = :'myvariable'
You can try to use a WITH clause.
WITH vars AS (SELECT 42 AS answer, 3.14 AS appr_pi)
SELECT t.*, vars.answer, t.radius*vars.appr_pi
FROM table AS t, vars;
Specifically for psql, you can pass psql variables from the command line too; you can pass them with -v. Here's a usage example:
$ psql -v filepath=/path/to/my/directory/mydatafile.data regress
regress=> SELECT :'filepath';
?column?
---------------------------------------
/path/to/my/directory/mydatafile.data
(1 row)
Note that the colon is unquoted, then the variable name its self is quoted. Odd syntax, I know. This only works in psql; it won't work in (say) PgAdmin-III.
This substitution happens during input processing in psql, so you can't (say) define a function that uses :'filepath' and expect the value of :'filepath' to change from session to session. It'll be substituted once, when the function is defined, and then will be a constant after that. It's useful for scripting but not runtime use.
FWIW, the real problem was that I had included a semicolon at the end of my \set command:
\set owner_password 'thepassword';
The semicolon was interpreted as an actual character in the variable:
\echo :owner_password
thepassword;
So when I tried to use it:
CREATE ROLE myrole LOGIN UNENCRYPTED PASSWORD :owner_password NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'infinity';
...I got this:
CREATE ROLE myrole LOGIN UNENCRYPTED PASSWORD thepassword; NOINHERIT CREATEDB CREATEROLE VALID UNTIL 'infinity';
That not only failed to set the quotes around the literal, but split the command into 2 parts (the second of which was invalid as it started with "NOINHERIT").
The moral of this story: PostgreSQL "variables" are really macros used in text expansion, not true values. I'm sure that comes in handy, but it's tricky at first.
postgres (since version 9.0) allows anonymous blocks in any of the supported server-side scripting languages
DO '
DECLARE somevariable int = -1;
BEGIN
INSERT INTO foo VALUES ( somevariable );
END
' ;
http://www.postgresql.org/docs/current/static/sql-do.html
As everything is inside a string, external string variables being substituted in will need to be escaped and quoted twice. Using dollar quoting instead will not give full protection against SQL injection.
You need to use one of the procedural languages such as PL/pgSQL not the SQL proc language.
In PL/pgSQL you can use vars right in SQL statements.
For single quotes you can use the quote literal function.
I solved it with a temp table.
CREATE TEMP TABLE temp_session_variables (
"sessionSalt" TEXT
);
INSERT INTO temp_session_variables ("sessionSalt") VALUES (current_timestamp || RANDOM()::TEXT);
This way, I had a "variable" I could use over multiple queries, that is unique for the session. I needed it to generate unique "usernames" while still not having collisions if importing users with the same user name.
Another approach is to (ab)use the PostgreSQL GUC mechanism to create variables. See this prior answer for details and examples.
You declare the GUC in postgresql.conf, then change its value at runtime with SET commands and get its value with current_setting(...).
I don't recommend this for general use, but it could be useful in narrow cases like the one mentioned in the linked question, where the poster wanted a way to provide the application-level username to triggers and functions.
I've found this question and the answers extremely useful, but also confusing. I had lots of trouble getting quoted variables to work, so here is the way I got it working:
\set deployment_user username -- username
\set deployment_pass '\'string_password\''
ALTER USER :deployment_user WITH PASSWORD :deployment_pass;
This way you can define the variable in one statement. When you use it, single quotes will be embedded into the variable.
NOTE! When I put a comment after the quoted variable it got sucked in as part of the variable when I tried some of the methods in other answers. That was really screwing me up for a while. With this method comments appear to be treated as you'd expect.
I really miss that feature. Only way to achieve something similar is to use functions.
I have used it in two ways:
perl functions that use $_SHARED variable
store your variables in table
Perl version:
CREATE FUNCTION var(name text, val text) RETURNS void AS $$
$_SHARED{$_[0]} = $_[1];
$$ LANGUAGE plperl;
CREATE FUNCTION var(name text) RETURNS text AS $$
return $_SHARED{$_[0]};
$$ LANGUAGE plperl;
Table version:
CREATE TABLE var (
sess bigint NOT NULL,
key varchar NOT NULL,
val varchar,
CONSTRAINT var_pkey PRIMARY KEY (sess, key)
);
CREATE FUNCTION var(key varchar, val anyelement) RETURNS void AS $$
DELETE FROM var WHERE sess = pg_backend_pid() AND key = $1;
INSERT INTO var (sess, key, val) VALUES (sessid(), $1, $2::varchar);
$$ LANGUAGE 'sql';
CREATE FUNCTION var(varname varchar) RETURNS varchar AS $$
SELECT val FROM var WHERE sess = pg_backend_pid() AND key = $1;
$$ LANGUAGE 'sql';
Notes:
plperlu is faster than perl
pg_backend_pid is not best session identification, consider using pid combined with backend_start from pg_stat_activity
this table version is also bad because you have to clear this is up occasionally (and not delete currently working session variables)
Variables in psql suck. If you want to declare an integer, you have to enter the integer, then do a carriage return, then end the statement in a semicolon. Observe:
Let's say I want to declare an integer variable my_var and insert it into a table test:
Example table test:
thedatabase=# \d test;
Table "public.test"
Column | Type | Modifiers
--------+---------+---------------------------------------------------
id | integer | not null default nextval('test_id_seq'::regclass)
Indexes:
"test_pkey" PRIMARY KEY, btree (id)
Clearly, nothing in this table yet:
thedatabase=# select * from test;
id
----
(0 rows)
We declare a variable. Notice how the semicolon is on the next line!
thedatabase=# \set my_var 999
thedatabase=# ;
Now we can insert. We have to use this weird ":''" looking syntax:
thedatabase=# insert into test(id) values (:'my_var');
INSERT 0 1
It worked!
thedatabase=# select * from test;
id
-----
999
(1 row)
Explanation:
So... what happens if we don't have the semicolon on the next line? The variable? Have a look:
We declare my_var without the new line.
thedatabase=# \set my_var 999;
Let's select my_var.
thedatabase=# select :'my_var';
?column?
----------
999;
(1 row)
WTF is that? It's not an integer, it's a string 999;!
thedatabase=# select 999;
?column?
----------
999
(1 row)
I've posted a new solution for this on another thread.
It uses a table to store variables, and can be updated at any time. A static immutable getter function is dynamically created (by another function), triggered by update to your table. You get nice table storage, plus the blazing fast speeds of an immutable getter.