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

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).

Related

Group by having clause in teradata

A teradata table
as
Group_categ id
A 1
A 2
A 3
A 5
A 8
A 9
B 11
C 1
C 2
C 3
C 4
need to filter it like
Group_categ min_id max _id
A 1 2
A 3 5
A 8 9
B 11 11
C 1 2
C 3 4
Seems you want to combine two consecutive rows into a single row:
SELECT Group_categ, Min(id), Max(id)
FROM
(
SELECT
Group_categ, id,
-- assign the same value to two consecutive rows: 0,0,1,1,2,2,..
-- -> used in the outer GROUP BY
(Row_Number() Over (PARTITION BY Group_categ ORDER BY id)-1) / 2 AS grp
FROM mytab
) AS dt
GROUP BY Group_categ, grp

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.

Counting the number of each unique value in regards to another column (oracle g11)

Hi I'm trying to do the following with a dataset I have that looks like this:
letter number
a 1
a 2
a 1
a 1
a 3
a 4
a 2
a 4
b 1
b 3
b 4
b 3
b 2
b 5
b 1
b 1
the dataset is the result of a sub query I have run. I want to format the data to look like this, so I can flag the ones that have a count higher than 3
letter number number_count
a 1 3
a 2 2
a 3 1
a 4 2
b 1 3
b 2 1
b 3 2
b 4 1
b 5 1
So for each value in the letter column, I need each unique number in col 2 counted up and displayed next to the value itself.
I've done a lot of searching to try and solve this and can't get any variation of count, count distinct, over or other code to work so I'm either underestimating the problem or more likely, I'm not sure how to phrase my search so I find the answer I'm looking for.
Any help would be greatly appreciated, I'm using sql developer to access an oracle 11g database.
So i think the following is what you are looking for:
select letter, number, count(letter) as number_count from table group by letter, number;
I added two more data lines to show an example where you'll have one result that's flagged.
WITH
sub_q AS (
SELECT 'a' AS letter_column, 1 AS value_column FROM Dual UNION ALL
SELECT 'a', 2 FROM Dual UNION ALL
SELECT 'a', 1 FROM Dual UNION ALL
SELECT 'a', 1 FROM Dual UNION ALL
SELECT 'a', 3 FROM Dual UNION ALL
SELECT 'a', 4 FROM Dual UNION ALL
SELECT 'a', 2 FROM Dual UNION ALL
SELECT 'a', 4 FROM Dual UNION ALL
SELECT 'b', 1 FROM Dual UNION ALL
SELECT 'b', 3 FROM Dual UNION ALL
SELECT 'b', 4 FROM Dual UNION ALL
SELECT 'b', 3 FROM Dual UNION ALL
SELECT 'b', 2 FROM Dual UNION ALL
SELECT 'b', 5 FROM Dual UNION ALL
SELECT 'b', 1 FROM Dual UNION ALL
SELECT 'b', 1 FROM Dual UNION ALL
SELECT 'b', 1 FROM Dual UNION ALL
SELECT 'b', 1 FROM DUal
)
SELECT
letter_column,
value_column,
COUNT(value_column) AS value_count,
CASE
WHEN COUNT(value_column) > 3 THEN 1
ELSE NULL
END AS flag_gt_3
FROM sub_q
GROUP BY
letter_column,
value_column
ORDER BY
letter_column,
value_column
;

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

find values which are the same as value above in sqlite

I am trying to find values which are the same as at least two values above it. Please take a look.
id number
1 2
2 6
3 7
4 7
5 7
6 1
7 2
8 4
9 7
So in this case select would return:
ID NUMBER
3 7
4 7
5 7
You can look up values in othe rows with a correlated subquery:
SELECT *
FROM MyTable
WHERE number = (SELECT number
FROM MyTable AS T2
WHERE T2.id = MyTable.id - 1)
AND number = (SELECT number
FROM MyTable AS T2
WHERE T2.id = MyTable.id - 2);

Resources