I have following three tables
table 1
mf_tab
id
seq
mfr
1234
1
goog
1234
2
apple
table 2
flg_tab
id
seq
flag
1234
1
REL
1234
2
REJ
table 3
pn_tab
id
seq
pn
1234
1
pxyz
1234
2
pabc
I would like the data in following format
id
Mfr1
mfr2
flag1
flag2
pn1
pn2
1234
goog
apple
REL
REJ
pxyz
pabc
Also I would have another case where all there columns are in one table as below. How to obtain smilar result from below table.
id
seq
Mfr
flag
pn
1234
1
goog
REL
pxyz
1234
2
apple
REJ
pabc
thanks
I can pivot one by one table but could not find a way to pivot multiple tables.
Thanks
An often used way to pivot is conditional aggregation:
select
id,
max(case when seq = 1 then t1.mfr end) as mfr1,
max(case when seq = 2 then t1.mfr end) as mfr2,
max(case when seq = 1 then t2.flag end) as flag1,
max(case when seq = 2 then t2.flag end) as flag2,
max(case when seq = 1 then t3.pn end) as pn1,
max(case when seq = 2 then t3.pn end) as pn2
from t1
join t2 using (id, seq)
join t3 using (id, seq)
group by id
order by id;
Related
following are the table in database.
many to many relations between train and passenger
table 1 name=train
TNO TNAMe
1 x
2 y
3 z
table 2 name=passenger
PNO PNAME
111 a
222 b
333 c
table 3 name=tp
TNO PNO TPDATE
1 111 23-NOV-15
2 222 24-JUN-14
3 222 19-JUN-13
1 333 23-NOV-15
using follwing code i only find out which train number has highest frequency
select tno,count(tno) as numberofoccurance from tp group by tno
Try to use group by and row_number analytical function as following:
Select name, tp_date
From
(Select t.tname tp.tp_date,
row_number() over (partition by tp.tp_date order by count(1) desc nulls last) as cnt
From train t
Join tp tp
On (t.id = tp.tno)
Group by tp.tno, tp.tp_date)
Where cnt = 1;
Cheers!!
I have table t:
ID Type
---- ----
1 a
1 b
2 a
2 a
3 b
And table with names of IDs from first table - n:
ID Name
---- ----
1 name1
2 name2
3 name3
I need make query in PL/SQL for count percentage of type occurrence among all types for same id (group by ID).
The result must be:
Name a% b% row
--- ---- --- ---
name1 50 50 1
name2 100 0 2
name3 0 100 3
I tried:
select
n.name,
a.perc as "a%",
b.perc as "b%",
row_number() over (
order by name asc
) mf_rownumber
from n n
left join
(select
id,
round(100 * (count(*) / sum(count(*)) over ()), 2) perc
from t
where (type = 'a')
group by id) a
on a.id = n.id
left join
(select
id,
round(100 * (count(*) / sum(count(*)) over ()), 2) perc
from t
where (type = 'b')
group by id) b
on b.id = n.id;
What I get, is percentage of every type from all rows:
Name a% b% row
--- ---- --- ---
name1 20 20 1
name2 40 0 2
name3 0 20 3
But I need count everything in borders of the same ID, not all rows.
I think it can be simplified a lot :
http://sqlfiddle.com/#!4/6bb2a/20
select
n.name,
round(100 * (sum(case when type='a' then 1 else 0 end) / count(*)), 2) as "a%",
round(100 * (sum(case when type='b' then 1 else 0 end) / count(*)), 2) as "b%",
row_number() over (order by name asc ) mf_rownumber
from n
left join t on t.id = n.id
group by n.name
I would do something like this:
select
n.name,
n.id,
count(case when type='a' then 1 end)/count(*)*100 as "a%",
count(case when type='b' then 1 end)/count(*)*100 as "b%"
from n left join t on a.id=n.id
group by n.id;
There are two table as below
Table1
ID Name Age Active PID
-----------------------------
1 A 2 Y 100
2 A 2 Y 100
3 A 2 Y 100
4 B 3 Y 200
5 B 3 Y 200
Table2
T2ID CID
---------
10 1
20 1
30 1
40 2
50 2
60 3
70 3
80 3
90 4
100 5
110 5
I am trying to inactivate the duplicate record of table 1 and reassign the table2 record to activated rows of table 1,The result for table1 and table2 should be as below
ID Name Age Active PID
-----------------------------
1 A 2 Y 100
2 A 2 N 100
3 A 2 N 100
4 B 3 N 200
5 B 3 Y 200
T2ID CID
---------
10 1
20 1
30 1
40 1
50 1
60 1
70 1
80 1
90 5
100 5
110 5
please help for oracle query to update
You can do this by using two merge statements, like so:
Update table2:
MERGE INTO table2 tgt
USING (WITH t1 AS (SELECT ID,
NAME,
age,
active,
pid,
MIN(ID) OVER (PARTITION BY pid) min_id,
CASE WHEN COUNT(CASE WHEN active = 'Y' THEN 1 END) OVER (PARTITION BY pid) > 1 THEN 'Y' ELSE 'N' END multi_active_rows
FROM table1)
SELECT t2.t2id,
t2.cid old_cid,
t1.min_id new_cid
FROM t1
INNER JOIN table2 t2 ON t1.id = t2.cid
WHERE t1.multi_active_rows = 'Y') src
ON (tgt.t2id = src.t2id)
WHEN MATCHED THEN
UPDATE SET tgt.cid = src.new_cid;
Update table1:
MERGE INTO table1 tgt
USING (WITH t1 AS (SELECT ID,
NAME,
age,
active,
pid,
MIN(ID) OVER (PARTITION BY pid) min_id,
CASE WHEN COUNT(CASE WHEN active = 'Y' THEN 1 END) OVER (PARTITION BY pid) > 1 THEN 'Y' ELSE 'N' END multi_active_rows
FROM table1)
SELECT ID
FROM t1
WHERE multi_active_rows = 'Y'
AND ID != min_id) src
ON (tgt.id = src.id)
WHEN MATCHED THEN
UPDATE SET active = 'N';
Since we want to derive the results to update both table1 and table2 from the original dataset in table1, it's easier to update table2 first before updating table1.
This works by finding the lowest id across each set of pids in table1, plus checking to see if there is more than one active row for each pid (there's no need to do any updates if we have at most one active row available).
Once we have that information, we can use that to decide which rows to update in each table, and we can use the min_id to update table2 with, and we can update any rows in table1 where the id doesn't match the min_id to be not active.
N.B. If you could have a mix of Ys and Ns in your data, you may need to skip the and id != min_id check in the second merge statement and amend the update part to update the row to Y if the id is the min_id, otherwise set it to N.
I have these tables:
Products, Articles, Product_Articles
Lets say, product_ids are: p1 , p2 article_ids are: a1 , a2 , a3
product_articles is:
(p1,a1)
(p1,a2)
(p2,a1)
(p2,a1)
(p2,a2)
(p2,a3)
How to query for product_id, which has only a1,a2, nothing less, nothing more?
UPDATED Try
SELECT p.*
FROM products p JOIN
(
SELECT product_id
FROM product_articles
GROUP BY product_id
HAVING COUNT(*) = SUM(CASE WHEN article_id IN (1, 2) THEN 1 ELSE 0 END)
AND SUM(CASE WHEN article_id IN (1, 2) THEN 1 ELSE 0 END) = 2
) q ON p.product_id = q.product_id
or
SELECT p.*
FROM products p JOIN
(
SELECT product_id, COUNT(*) a_count
FROM product_articles
WHERE article_id IN (1, 2)
GROUP BY product_id
HAVING COUNT(*) = 2
) a ON p.product_id = a.product_id JOIN
(
SELECT product_id, COUNT(*) total_count
FROM product_articles
GROUP BY product_id
) b ON p.product_id = b.product_id
WHERE a.a_count = b.total_count
Here is SQLFiddle demo for both queries
This is an example of a "set-within-sets" subquery. I advocate using aggregation with a having clause for the logic, because this is the most general way to express the relationships.
The idea is that you can count the appearance of the articles within a product (in this case) in a way similar to using a where statement. The code is a bit more complex, but it offers flexibility. In your case, this would be:
select pa.product_id
from product_articles pa
group by pa.product_id
having sum(case when pa.article_id = 'a1' then 1 else 0 end) > 0 and
sum(case when pa.article_id = 'a2' then 1 else 0 end) > 0 and
sum(case when pa.article_id not in ('a1', 'a2') then 1 else 0 end) = 0;
The first two clauses count the appearance of the two articles, making sure that there is at least one occurrence of each. The last counts the number of rows without those two articles, making sure there are none.
You can see how this easily generalizes to more articles. Or to queries where you have "a1" and "a2" but not "a3". Or where you have three of four of specific articles, and so on.
I believe this can be done entirely using relational joins, as follows:
SELECT DISTINCT pa1.PRODUCT_ID
FROM PRODUCT_ARTICLES pa1
INNER JOIN PRODUCT_ARTICLES pa2
ON (pa2.PRODUCT_ID = pa1.PRODUCT_ID)
LEFT OUTER JOIN (SELECT *
FROM PRODUCT_ARTICLES
WHERE ARTICLE_ID NOT IN (1, 2)) pa3
ON (pa3.PRODUCT_ID = pa1.PRODUCT_ID)
WHERE pa1.ARTICLE_ID = 1 AND
pa2.ARTICLE_ID = 2 AND
pa3.PRODUCT_ID IS NULL
SQLFiddle here.
The inner join looks for products associated with the articles we care about (articles 1 and 2 - produces product 1 and 2). The left outer looks for products associated with articles we don't care about (anything article except 1 and 2) and then only accepts products which don't have any unwanted articles (i.e. pa3.PRODUCT_ID IS NULL, indicating that no row from pa3 was joined in).
I'm using Oracle 10g.
Question: How can I write query to return just ID only if ALL the codes for that ID end in 6? I don't want ID=1 because not all its codes end in 6.
TABLE_A
ID Code
===============
1 100
1 106
2 206
3 316
3 326
4 444
Desired Result:
ID
==
2
3
You simply want each ID where the count of rows for that id is the same as the count of rows where the third digit is six.
SELECT ID
FROM TABLE_A
GROUP BY ID
HAVING COUNT(*) = COUNT(CASE WHEN SUBSTR(code,3,1) = '6' THEN 1 END)
Try this:
SELECT DISTINCT b.id
FROM (
SELECT id,
COUNT(1) cnt
FROM table_a
GROUP BY id
) a,
(
SELECT id,
COUNT(1) cnt
FROM table_a
WHERE CODE LIKE '%6'
GROUP BY id
)b
WHERE a.id = b.id
AND a.cnt = b.cnt
Alternative using ANALYTIC functions:
SELECT DISTINCT id
FROM
(
SELECT id,
COUNT(1) OVER(PARTITION BY id) cnt,
SUM(CASE WHEN code LIKE '%6' THEN 1 ELSE 0 END) OVER(PARTITION BY id) sm
FROM table_a
)
WHERE cnt = sm