How to transpose from rows to columns in PL/SQL? - plsql

I have a table with data like the following:
DESC_E
RPTFIELD
desc 1
TITLE1
desc 2
TITLE2
desc 3
TITLE3
What is the best way to get result like this?
TITLE1
TITLE2
TITLE3
desc 1
desc 2
desc 3

A simple option is
(with sample data you posted)
SQL> with test (desc_e, rptfield) as
2 (select 'desc 1', 'TITLE1' from dual union all
3 select 'desc 2', 'TITLE2' from dual union all
4 select 'desc 3', 'TITLE3' from dual
5 )
Query:
6 select max(case when rptfield = 'TITLE1' then desc_e end) as title1,
7 max(case when rptfield = 'TITLE2' then desc_e end) as title2,
8 max(case when rptfield = 'TITLE3' then desc_e end) as title3
9 from test;
TITLE1 TITLE2 TITLE3
------ ------ ------
desc 1 desc 2 desc 3
SQL>

Littlefoot's answer is good. Just to add, you can write this more concisely with DECODE:
SELECT MAX(DECODE(rptfield,'TITLE1',desc_e)) title1,
MAX(DECODE(rptfield,'TITLE2',desc_e)) title2,
MAX(DECODE(rptfield,'TITLE3',desc_e)) title3
FROM test

Related

SQL: select items from one table only if in other table they have both columns with same value

I need some hints.. I got a tables which look like that:
ID
ITEM
1
XXXX
2
YYYY
3
ZZZZ
ID
ID_2
SUBITEM
1
10
AA
1
11
BB
2
12
CC
2
13
DD
3
14
EE
3
15
FF
3
16
GG
ID_2
value
10
1
11
0
12
1
13
1
14
1
15
1
16
0
I need to get all items where ALL sub-items are = 1.
for example XXXX should not be listed, because BB has value 0.
select distinct
(table1.item)
from table1,
table2,
table3
where table1.id = table2.id
and table2.id_2 = table3.id_2
and table3.value = 1
order by table1.item
my code gives me all items wherever 1 is a value
Thanks for help!
I would use an aggregation approach here:
SELECT i.ID, i.ITEM
FROM items i
INNER JOIN subitems s ON s.ID = i.ID
INNER JOIN vals v ON v.ID_2 = s.ID_2
GROUP BY i.ID, i.ITEM
HAVING COUNT(CASE WHEN v.value != 1 THEN 1 END) = 0;
The COUNT expression above will count 1 every time a value other than 1 appears for a given item. A matching item, then, is one whose non 1 count is zero, meaning all values are equal to 1.
Then zzzz shouldn't be in result set either, as GG has value 0.
Sample data:
SQL> with
2 a (id, item) as
3 (select 1, 'xxxx' from dual union all
4 select 2, 'yyyy' from dual union all
5 select 3, 'zzzz' from dual
6 ),
7 b (id, id_2, subitem) as
8 (select 1, 10, 'aa' from dual union all
9 select 1, 11, 'bb' from dual union all
10 select 2, 12, 'cc' from dual union all
11 select 2, 13, 'dd' from dual union all
12 select 3, 14, 'ee' from dual union all
13 select 3, 15, 'ff' from dual union all
14 select 3, 16, 'gg' from dual
15 ),
16 c (id_2, value) as
17 (select 10, 1 from dual union all
18 select 11, 0 from dual union all
19 select 12, 1 from dual union all
20 select 13, 1 from dual union all
21 select 14, 1 from dual union all
22 select 15, 1 from dual union all
23 select 16, 0 from dual
24 )
Query begins here:
25 select a.item
26 from a join b on a.id = b.id
27 join c on c.id_2 = b.id_2
28 group by a.item
29 having min(c.value) = max(c.value)
30 and min(c.value) = 1;
ITEM
----
yyyy
SQL>
In other words: join all three tables. As having clause is to be used, group by the item column and set conditions (you said that all values must be 1).

Mariadb Increment counter with ORDER BY

I Have table with description
Table Name : mt_user
column : id , name
Records
id
name
1
Tom
10
Carren
30
Jessy
Then I Query:
select r.id,
#rownum := #rownum + 1 as rownum
from mt_user r
cross join
(SELECT
#rownum := 0
) params
order by r.id desc;
the result is :
id
rownum
30
3
10
2
1
1
But if the query :
select r.id,
#rownum := #rownum + 1 as rownum
from mt_user r
cross join
(SELECT
#rownum := 0
) params
order by r.id asc;
the result is:
id
rownum
1
1
10
2
30
3
the question is, how can I achieved the result like this
id
rownum
1
1
10
2
30
3
but with order by r.id desc
because I trying the query in mariadb version 10.5.9, it have no problem.
it act strange on mariadb version 10.6.10 or 10.9.2

how do i update mulitple rows with dynamic data?

I am using SQLite to update mulipte rows, but it does not work.
item table to keep the item data
item_ID Qty
- -
1 10
2 10
3 10
user_basket table to keep users' basket data
user_ID item_ID Bask_Qty
- - -
1 1 5
1 2 1
2 1 1
I used command like:
UPDATE item
SET Qty =
(SELECT Qty-Bask_Qty FROM user_basket
INNER JOIN item ON item.item_ID = user_basket.item_ID)
WHERE item_ID IN (SELECT item_ID FROM user_basket WHERE user_ID = 1);
After the command, I should expect item table be like that:
item_ID Qty
- -
1 5
2 9
3 10
but instead, I got:
item_ID Qty
- -
1 5
2 5
3 10
apparently, it used the same value to update all the rows.
Use a correlated subquery:
UPDATE item AS i
SET Qty = i.Qty - COALESCE(
(SELECT SUM(b.Bask_Qty) FROM user_basket b WHERE b.user_ID = 1 AND b.item_ID = i.item_ID),
0
)
See the demo.
Or, with a WHERE clause to avoid unnecessary updates:
UPDATE item AS i
SET Qty = i.Qty - (SELECT SUM(b.Bask_Qty) FROM user_basket b WHERE b.user_ID = 1 AND b.item_ID = i.item_ID)
WHERE EXISTS (SELECT 1 FROM user_basket b WHERE b.user_ID = 1 AND b.item_ID = i.item_ID)
See the demo.
If your version of SQLite is 3.33.0+ you can use UPDATE...FROM syntax:
UPDATE item AS i
SET Qty = i.Qty - b.Bask_Qty
FROM (SELECT item_ID, SUM(Bask_Qty) Bask_Qty FROM user_basket WHERE user_id = 1 GROUP BY item_ID) AS b
WHERE b.item_id = i.item_id
In all of the above queries I used SUM(Bask_Qty) to return the quantity of each item from the table user_basket, just in case there are more than 1 rows for each item.
If you are sure that there may be only 1 row for each item then replace it with just Bask_Qty.

Find empty positions in a grid

In a Contents table, items are stored in X,Y coordinates:
Contents
-------
id
parent_id
pos_x
pos_y
Assume the container size is 3 by 3. I'd like to find which positions in a given container are free. So far I've generated a 2D matrix:
SELECT *
FROM
(SELECT rownum X FROM dual CONNECT BY LEVEL <= 3 ) xaxis
INNER JOIN
(SELECT rownum Y FROM dual CONNECT BY LEVEL <=3 ORDER BY 1) yaxis
ON xaxis.X <> yaxis.Y OR xaxis.X = yaxis.Y
Then I attempt to JOIN the queries together, excluding X,Y positions present in Contents:
SELECT X, Y
FROM
(SELECT rownum X FROM dual CONNECT BY LEVEL <= 3 ) xaxis
INNER JOIN
(SELECT rownum Y FROM dual CONNECT BY LEVEL <=3 ORDER BY 1) yaxis
ON xaxis.X <> yaxis.Y OR xaxis.X = yaxis.Y
INNER JOIN (
SELECT pos_x, pos_y FROM Contents WHERE parent_id = ?) items
ON items.posx <> xaxis.X AND items.posy <> yaxis.Y;
This doesn't treat each pair as unique, and excludes values from all rows if a position is occupied. For example, assuming that (2, 2) is occupied, the above returns:
X Y
-----
1 1
1 3
3 1
3 3
Essentially I'm trying to get the difference of the two sets. Any help appreciated.
I figured out the answer right before I posted the question, so I thought I'd post it and answer it at the same time. Stating the problem as get the difference of the two sets set me in the right direction.
The answer is the MINUS operator. Replace the final JOIN with MINUS and you get the intended results:
select X, Y
from
(select rownum X from dual CONNECT BY LEVEL <= 3 ) xaxis
inner join
(select rownum Y from dual CONNECT BY LEVEL <=3 order by 1) yaxis
on xaxis.X <> yaxis.Y OR xaxis.X = yaxis.Y
MINUS
select pos_x, pos_y FROM Contents WHERE parent_id = ?;
which returns the intended result (note the lack of (2, 2)):
X Y
-----
1 1
1 2
1 3
2 1
2 3
3 1
3 2
3 3
Today was a good day
You could do this with an outer join rather than a minus (although you'd have to test both to find out which is more performant for your data!).
If you are only doing it for a single parent_id at a time, you would do:
WITH CONTENTS AS (SELECT 1 parent_id, 2 pos_x, 2 pos_y FROM dual UNION ALL
SELECT 2 parent_id, 2 pos_x, 1 pos_y FROM dual)
SELECT xaxis.x,
yaxis.y
FROM ((SELECT LEVEL x FROM dual CONNECT BY LEVEL <= 3) xaxis
CROSS JOIN (SELECT LEVEL y FROM dual CONNECT BY LEVEL <= 3) yaxis)
LEFT OUTER JOIN CONTENTS c ON c.pos_x = xaxis.x AND c.pos_y = yaxis.y AND c.parent_id = 1
WHERE c.parent_id IS NULL
ORDER BY x, y;
X Y
---------- ----------
1 1
1 2
1 3
2 1
2 3
3 1
3 2
3 3
Alternatively, if you want to run it for all parent_ids, you could use a partitioned outer join like so:
WITH CONTENTS AS (SELECT 1 parent_id, 2 pos_x, 2 pos_y FROM dual UNION ALL
SELECT 2 parent_id, 2 pos_x, 1 pos_y FROM dual)
SELECT c.parent_id,
xaxis.x,
yaxis.y
FROM ((SELECT LEVEL x FROM dual CONNECT BY LEVEL <= 3) xaxis
CROSS JOIN (SELECT LEVEL y FROM dual CONNECT BY LEVEL <= 3) yaxis)
LEFT OUTER JOIN CONTENTS c PARTITION BY (c.parent_id) ON c.pos_x = xaxis.x AND c.pos_y = yaxis.y
WHERE c.pos_x IS NULL
AND c.pos_y IS NULL
ORDER BY c.parent_id,
xaxis.x,
yaxis.y;
PARENT_ID X Y
---------- ---------- ----------
1 1 1
1 1 2
1 1 3
1 2 1
1 2 3
1 3 1
1 3 2
1 3 3
2 1 1
2 1 2
2 1 3
2 2 2
2 2 3
2 3 1
2 3 2
2 3 3

i have one Issue in My Query not get Group Record

hi i have one issue in get query like 3 column
ID Grp Name
1 10 aa
2 11 bb
3 11 cc
4 11 dd
5 12 ee
6 12 ff
i want ans is
ID Grp Name
1 10 aa
2 11 bb
5 12 ee
so what can id Do ?
In sql server
Try this !
select * from
(
select *,rn=row_number()over(partition by Grp order by ID) from table
)x
where x.rn=1
For SQL Server:
WITH CTE AS (SELECT *,RN=ROW_NUMBER() OVER(PARTITION BY Grp ORDER BY ID)
FROM TableName)
SELECT ID,Grp,Name
FROM CTE
WHERE RN=1
Example in SQL Fiddle.
For MySQL:
SELECT ID,Grp,Name
FROM
(SELECT A.ID, A.Grp, A.Name, count(*) as row_number FROM TableName A
JOIN TableName B ON A.Grp = B.Grp AND A.ID >= B.ID
GROUP BY A.Grp, A.ID,A.Name) T
WHERE row_number=1
Example in SQL Fiddle.

Resources