UNION table in second query based on the result of first query in Oracle sql - sql

I am struggling with following problem (in further I provide pseudocode in order to make my example more understandable)
Assume I have 2 queries which result I want to union with each other
What query1 output looks like:
ID OFFER
1 prod_1
2 prod_2
3 prod_2
4 prod_1
What query2 output looks like:
ID SEGMENT
1 LOW
2 HIGH
3 MED
999 MED
What I need to do is to union results of this 2 queries, but avoid taking row with ID = 999
If there any way to do it using UNION by extracting from query2 rows bases on values of column ID which are present in column ID of query2?
I know that following code is incorrect but it conveys the idea of ​​the question greatly:
--query1
(
SELECT ID, OFFER
FROM TAB1
WHERE ID <= 4
) RES1
UNION
--query2
SELECT ID, SEGMENT
FROM TAB1
WHERE ID IN (SELECT ID FROM RES2)
Result should be as following
ID OFFER
1 prod_1
2 prod_2
3 prod_2
4 prod_1
1 LOW
2 HIGH
3 MED
Appreciate your help

Your pseudo code comes very close. You can use WITH for convenience:
WITH q1 AS (SELECT id, offer FROM tab1 WHERE id <= 4)
, q2 AS (SELECT id, segment FROM tab1 WHERE id IN (SELECT id FROM q1))
SELECT * FROM q1
UNION ALL
SELECT * FROM q2;
(Be aware though that you can get the rows in any order, if you don't specify an ORDER BY clause.)

Related

Snowflake: Repeating rows based on column value

How to repeat rows based on column value in snowflake using sql.
I tried a few methods but not working such as dual and connect by.
I have two columns: Id and Quantity.
For each ID, there are different values of Quantity.
So if you have a count, you can use a generator:
with ten_rows as (
select row_number() over (order by null) as rn
from table(generator(ROWCOUNT=>10))
), data(id, count) as (
select * from values
(1,2),
(2,4)
)
SELECT
d.*
,r.rn
from data as d
join ten_rows as r
on d.count >= r.rn
order by 1,3;
ID
COUNT
RN
1
2
1
1
2
2
2
4
1
2
4
2
2
4
3
2
4
4
Ok let's start by generating some data. We will create 10 rows, with a QTY. The QTY will be randomly chosen as 1 or 2.
Next we want to duplicate the rows with a QTY of 2 and leave the QTY =1 as they are.
Obviously you can change all parameters above to suit your needs - this solution works super fast and in my opinion way better than table generation.
Simply stack SPLIT_TO_TABLE(), REPEAT() with a LATERAL() join and voila.
WITH TEN_ROWS AS (SELECT ROW_NUMBER()OVER(ORDER BY NULL)SOME_ID,UNIFORM(1,2,RANDOM())QTY FROM TABLE(GENERATOR(ROWCOUNT=>10)))
SELECT
TEN_ROWS.*
FROM
TEN_ROWS,LATERAL SPLIT_TO_TABLE(REPEAT('hire me $10/hour',QTY-1),'hire me $10/hour')ALTERNATIVE_APPROACH;

Oracle: union all query 1 and query 2 want to minus some rows if query 1 have rowdata

my query as below , i want to minus some rows from query1 when query2 have rowdata , but i don't know how to do:
my query:
with query1 as(
select wm_concat(linkman_name) name,
wm_concat(phone_num) phone,
t.org_id
from (
select linkman_name, phone_num, LINK_ORG_ID, org_id
from TD_SM_LINKMAN
where STATE = '2'
and (LINK_ORG_ID is null or LINK_ORG_ID = '')) t
group by t.org_id) ,
query2 as(
select wm_concat(linkman_name) name,
wm_concat(phone_num) phone,
org_id
from (select linkman_name, phone_num, LINK_ORG_ID, org_id
from TD_SM_LINKMAN
where STATE = '2'
and (LINK_ORG_ID = '55')) t
group by org_id)
select *
from query1
union all
select *
from query2 minus
-- this doesn't work ,i want to minus the rowdata from query 1 when query1.org_id = query2.org_id. the query2 is marked as outer query column.
(select * from query1 where query1.ORG_ID = query2.ORG_ID)
;
sample table
name phone link_org_id org_id
lily 133 1
ming 144 1
hao 333 2
jane 1234 55 2
bob 666 3
herry 555 3
query 1 result:
name phone org_id
lily,ming 133,144 1
hao 333 2
bob,herry 666,555 3
query 2 result:
name phone org_id
jane 1234 2
such like this , jane selected by query2 and hao selected by query 1 . All of them are from a same org which org_id =2 . but i don't need hao ,i just need jane. how to do?
i means if query2 can find result , then no need query1's result. but if query2 can't find any data, then i need query1's data.
The way it is now, you'll first have to split names (and phones) into rows, and then apply set operators (UNION, MINUS) to such a data.
Which means that you shouldn't use WM_CONCAT at all; at least, not at the beginning, because
first you concatenate data
then you'd have to split it back into rows
UNION / MINUS sets
Doing useless job in the first 2 steps.
I'd suggest you to UNION / MINUS data first, then aggregate them using WM_CONCAT. By the way, which database version do you use? WM_CONCAT is a) undocumented, b) doesn't even exist in latest Oracle database versions so you'd rather switch to LISTAGG, if possible.

Oracle SQL: Limiting multiple where clauses

Apologies if this seems like a duplicate to this question but I believe my use case is slightly different.
I have two tables.
Table1
ID INTCODE
-----------------------------
000019827364 1
000019829201 2
890418392101 3
890418390395 4
890418398677 5
505586578932 6
505586578914 7
505586578933 8
505586578012 9
490201827383 10
490201827466 11
001952046578 12
Table2
INTCODE Category
-------------------------
1 Display
2 Display
3 Display
4 Display
5 Display
6 Audio
7 Audio
8 Audio
9 Audio
10 Audio
11 Audio
12 Audio
My expected query results are all possible 5 digit prefixes of each category and in each of these prefixes - I want to extract at least 2 full IDs. Below is an example if I had a where clause for category as 'Display'.
ID PREFIX Category ID
-----------------------------------------------
00001 Display 000019827364
00001 Display 000019829201
89041 Display 890418392101
89041 Display 890418390395
The query I currently have is
SELECT
SUBSTR(t1.ID, 1, 5)
FROM
table1 t1
,table2 t2
WHERE
AND UPPER(t2.category) = 'DISPLAY'
AND t2.REGION_ID = 1
AND t2.ZONE_ID = 2
AND t1.REGION_ID = 1
AND t1.ZONE_ID = 2
AND t1.INTCODE = t2.INTCODE
GROUP BY
SUBSTR(t1.ID, 1, 5)
I am now kind of lost. Should I be running another query where I say
t1.ID LIKE '00001%'
OR LIKE '89041%'
This list will go on to be huge cause some of the categories have 400-500 prefixes. Is there a better way to go about this? Possibly in a single query?
I'm using Oracle SQL.
Many thanks!
You can use row_number() for this:
select Category, ID, IDPrefix
from (select Category, ID, SUBSTR(ID, 1, 5) as IDPREFIX,
ROW_NUMBER() OVER (PARTITION BY SUBSTR(ID, 1, 5) ORDER BY ID) as seqnum
FROM table1 JOIN
table2 t2
ON t1.INTCODE = t2.INTCODE ANd
t1.Region_id = t2.Region_id and
t1.zone_id = t2.zone_id
WHERE UPPER(t2.category) = 'DISPLAY'
) t
WHERE seqnum <= 2;
Assuming you can to display two rows with different Id, without any more constraint, you could simply use an union where the first query would select the max id, and the second query the min id.
So your query would look something like this
select id_prefix, category, max(id)
from yourTable
union
select id_prefix, category, min(id)
from yourTable
Now simply add to this algorithm your where conditions.

Exclude value of a record in a group if another is present v2

In the example table below, I'm trying to figure out a way to sum amount over marks in two situations: the first, when mark 'C' exists within a single id, and the second, when mark 'C' doesn't exist within an id (see id 1 or 2). In the first situation, I want to exclude the amount against mark 'A' within that id (see id 3 in the desired conversion table below). In the second situation, I want to perform no exclusion and take a simple sum of the amounts against the marks.
In other words, for id's containing both mark 'A' and 'C', I want to make the amount against 'A' as zero. For id's that do not contain mark 'C' but contain mark 'A', keep the original amount against mark 'A'.
My desired output is at the bottom. I've considered trying to partition over id or use the EXISTS command, but I'm having trouble conceptualizing the solution. If any of you could take a look and point me in the right direction, it would be greatly appreciated :)
example table:
id mark amount
------------------
1 A 1
2 A 3
2 B 2
3 A 1
3 C 3
desired conversion:
id mark amount
------------------
1 A 1
2 A 3
2 B 2
3 A 0
3 C 3
desired output:
mark sum(amount)
--------------------
A 4
B 2
C 3
You could slightly modify my previous answer and end up with this:
SELECT
mark,
sum(amount) AS sum_amount
FROM atable t
WHERE mark <> 'A'
OR NOT EXISTS (
SELECT *
FROM atable
WHERE id = t.id
AND mark = 'C'
)
GROUP BY
mark
;
There's a live demo at SQL Fiddle.
Try:
select
mark,
sum(amount)
from ( select
id,
mark,
case
when (mark = 'A' and id in (select id from table where mark = 'C')) then 0
else amount
end as amount
from table ) t1
group by mark

Returning several rows from a single query, based on a value of a column

Let's say I have this table:
|Fld | Number|
1 5
2 2
And I want to make a select that retrieves as many Fld as the Number field has:
|Fld |
1
1
1
1
1
2
2
How can I achieve this? I was thinking about making a temporary table and instert data based on the Number, but I was wondering if this could be done with a single Select statement.
PS: I'm new to SQL
You can join with a numbers table:
SELECT Fld
FROM yourtable
JOIN Numbers
ON yourtable.Number <= Numbers.Number
A numbers table is just a table with a list of numbers:
Number
1
2
3
etc...
Not an great solution (since you still query your table twice, but maybe you can work from it)
SELECT t1.fld, t1.number
FROM table t1, (
SELECT ROWNUM number FROM dual
CONNECT BY LEVEL <= (SELECT MAX(number) FROM t1)) t2
WHERE t2.number<=t1.number
It generates maximum amount of rows needed and then filters it by each row.
I don't know if your RDBMS version supports it (although I rather suspect it does), but here is a recursive version:
WITH remaining (fld, times) as (SELECT fld, 1
FROM <table>
UNION ALL
SELECT a.fld, a.times + 1
FROM remaining as a
JOIN <table> as b
ON b.fld = a.fld
AND b.number > a.times)
SELECT fld
FROM remaining
ORDER BY fld
Given your source data table, it outputs this (count included for verification):
fld times
=============
1 1
1 2
1 3
1 4
1 5
2 1
2 2