string_agg function with IN operator not working in PostgreSQL Query - postgresql-9.1

here is my query
select *
from table
where id in ( select string_agg(CAST(id as varchar), '","') FROM table)

string_agg() is completely useless and unnecessary for that:
select *
from table_one
where id in (select id FROM other_table)
I assume you are doing that for two different tables, otherwise that would be a very expensive way of writing: select * from table where id is not null

Related

Get LIMIT value from subquery result

I would like to use the LIMIT option in my query, but the number of expected rows is stored in another table. This is what I have, but it doesn't work:
select * from table1 limit (select limitvalue from table2 where id = 1)
When I only run the subquery, the result is 6, as expected.
I prefer working with a WITH statement if possible, but that didn't work eiter.
Thank you in advance!
You could use a prepared statement to get the limit of queries from the other table because the limit clause does not allow non constant variables as parameter:
PREPARE firstQuery FROM "SELECT * FROM table1 LIMIT ?";
SET #limit = (select limitvalue from table2 where id = 1);
EXECUTE firstQuery USING #limit;
The source of the sql query from another post
You can make use of MariaDB's ROW_NUMBER function in a CTE to count the rows to be output, comparing that against the limitvalue. For example:
WITH rownums AS (
SELECT *,
ROW_NUMBER() OVER () AS rn
FROM table1
)
SELECT *
FROM rownums
WHERE rn <= (SELECT limitvalue FROM table2 WHERE id = 1)
Note Using LIMIT without ORDER BY is not guaranteed to give you the same results every time. You should include an ORDER BY clause in the OVER part of the ROW_NUMBER window function. With the sample data in my demo, you might use something like:
ROW_NUMBER() OVER (ORDER BY mark DESC)
Demo on dbfiddle

SQLite UNION right only if left is empty

In SQLite there is no such function to UNION only if left expression results empty set.
Consider these as two base tables test and test2:
create table test(
id integer not null primary key,
val integer not null
);
insert into test values(1, 10);
insert into test values(2, 20);
insert into test values(3, 30);
create table test2(
id integer not null primary key,
val integer not null
);
insert into test2 values(1, 100);
insert into test2 values(2, 200);
insert into test2 values(3, 300);
I have this simple query:
select * from test
union
select * from test2;
I want this to always query the first select and the second if (and only if) the first gives empty result.
To illustrate:
select * from test
union
select * from test2;
This shall return all rows from test, and then quit: don't touch test2 at all.
Another sample:
select * from test where val > 50
union
select * from test2;
First query gives empty results, move on and do the second select, shall result all rows from test2.
I want this to be as fast as possible => therefore I don't want to add a subquery for the second select.
Here is the playground.
A SQL SELECT is declarative. It expresses what you want, not how to obtain it. It is up to the db engine to convert it to a query plan. Some SQL dialects allow to hint the engine to do things a certain way, but not SQLite.
The only solution is to query the two part from an imperative language (python, java, C#, ...) that allows you to implements the do something first and check result then do something else
I don't want to add a subquery for the second select.
Well, the two queries are functionally independent in the query, and there is no way for the second query to tell that the first one came up empty, unless you use a subquery.
So, you would typically use a not exists condition in the second query, with a subquery that matches the first query.
So:
select * from test
union all
select * from test2 where not exists (select 1 from test)
Or if there is a where clause in the first query:
select * from test where val > 50
union all
select * from test2 where not exists (select 1 from test where val > 50)

Give alias to anonymous column of global collection type in SELECT FROM TABLE(collection)

I'm trying to use values from an apex_t_numbers collection in a SELECT query as WITH subquery:
atn_cur_ids := apex_string.split_numbers(arg_v_ids, ':');
-- So if arg_v_ids := '1:2:3', then atn_cur_ids := apex_t_numbers(1, 2, 3)
with t_cur_ids as (
select * as id from table(atn_cur_ids);
)
select text from t_texts
join t_cur_ids on t_texts.id = t_cur_ids.id
And here's the problem - apex_t_numbers is a table of number, not of record type with named fields. Oracle SQL doesn't allow to give aliases to a * even if it has only one single "anonymous" column.
A possible solution could be a function that both
receives * and
returns value per row
But I am aware of only one that can get a * and return something - count(*), and it doesn't meet the second requirement.
Alright, found a solution. It could be done with column_value as a name of column:
with t_cur_ids as (
select column_value as id from table(atn_cur_ids);
)
select text from t_texts
join t_cur_ids on t_texts.id = t_cur_ids.id

Is it possible to use WHERE clause in same query as PARTITION BY?

I need to write SQL that keeps only the minimum 5 records per each identifiable record in a table. For this, I use partition by and delete all records where the value returned is greater than 5. When I attempt to use the WHERE clause in the same query as the partition by statement, I get the error "Ordered Analytical Functions not allowed in WHERE Clause". So, in order to get it to work, I have to use three subqueries. My SQL looks ilke this:
delete mydb.mytable where (field1,field2) in
(
select field1,field2 from
(
select field1,field2,
Rank() over
(
partition BY field1
order by field1,field2
) n
from mydb.mytable
) x
where n > 5
)
The innermost subquery just returns the raw data. Since I can't use WHERE there, I wrapped it with a subquery, the purpose of which is to 1) use WHERE to get records greater than 5 in rank and 2) select only field1 and field2. The reason why I select only those two fields is so that I can use the IN statement for deleting those records in the outermost query.
It works, but it appears a bit cumbersome. I'd like to consolidate the inner two subqueries into a single subquery. Is this possible?
Sounds like you need to use the QUALIFY clause which is the HAVING clause for Window Aggregate functions. Below is my take on what you are trying to accomplish.
Please do not run this SQL directly against your production data without first testing it.
/* Physical Delete */
DELETE TGT
FROM MyDB.MyTable TGT
INNER JOIN
(SELECT Field1
, Field2
FROM MyDB.MyTable
QUALIFY ROW_NUMBER() (PARTITION BY Field1, ORDER BY Field1,2)
> 5
) SRC
ON TGT.Field1 = SRC.Field1
AND TGT.Field2 = SRC.Fileld2
/* Logical Delete */
UPDATE TGT
FROM MyDB.MyTable TGT
,
(SELECT Field1
, Field2
FROM MyDB.MyTable
QUALIFY ROW_NUMBER() (PARTITION BY Field1, ORDER BY Field1,2)
> 5
) SRC
SET Deleted = 'Y'
/* RecordExpireDate = Date - 1 */
WHERE TGT.Field1 = SRC.Field1
AND TGT.Field2 = SRC.Fileld2

PL/SQL - comma separated list within IN CLAUSE

I am having trouble getting a block of pl/sql code to work. In the top of my procedure I get some data from my oracle apex application on what checkboxes are checked. Because the report that contains the checkboxes is generated dynamically I have to loop through the
APEX_APPLICATION.G_F01
list and generate a comma separated string which looks like this
v_list VARCHAR2(255) := (1,3,5,9,10);
I want to then query on that list later and place the v_list on an IN clause like so
SELECT * FROM users
WHERE user_id IN (v_list);
This of course throws an error. My question is what can I convert the v_list to in order to be able to insert it into a IN clause in a query within a pl/sql procedure?
If users is small and user_id doesn't contain commas, you could use:
SELECT * FROM users WHERE ',' || v_list || ',' LIKE '%,'||user_id||',%'
This query is not optimal though because it can't use indexes on user_id.
I advise you to use a pipelined function that returns a table of NUMBER that you can query directly. For example:
CREATE TYPE tab_number IS TABLE OF NUMBER;
/
CREATE OR REPLACE FUNCTION string_to_table_num(p VARCHAR2)
RETURN tab_number
PIPELINED IS
BEGIN
FOR cc IN (SELECT rtrim(regexp_substr(str, '[^,]*,', 1, level), ',') res
FROM (SELECT p || ',' str FROM dual)
CONNECT BY level <= length(str)
- length(replace(str, ',', ''))) LOOP
PIPE ROW(cc.res);
END LOOP;
END;
/
You would then be able to build queries such as:
SELECT *
FROM users
WHERE user_id IN (SELECT *
FROM TABLE(string_to_table_num('1,2,3,4,5'));
You can use XMLTABLE as follows
SELECT * FROM users
WHERE user_id IN (SELECT to_number(column_value) FROM XMLTABLE(v_list));
I have tried to find a solution for that too but never succeeded. You can build the query as a string and then run EXECUTE IMMEDIATE, see http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/dynamic.htm#i14500.
That said, it just occurred to me that the argument of an IN clause can be a sub-select:
SELECT * FROM users
WHERE user_id IN (SELECT something FROM somewhere)
so, is it possible to expose the checkbox values as a stored function? Then you might be able to do something like
SELECT * FROM users
WHERE user_id IN (SELECT my_package.checkbox_func FROM dual)
Personally, i like this approach:
with t as (select 'a,b,c,d,e' str from dual)
--
select val
from t, xmltable('/root/e/text()'
passing xmltype('<root><e>' || replace(t.str,',','</e><e>')|| '</e></root>')
columns val varchar2(10) path '/'
)
Which can be found among other examples in Thread: Split Comma Delimited String Oracle
If you feel like swamping in even more options, visit the OTN plsql forums.

Resources