Find empty positions in a grid - oracle11g

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

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

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.

SQLite - Count Distinct with three column groupings ignores last grouping

I'm working on a database about directors from US companies. One specific table I've created from a query contains all directors who were in a company 5 years before it declared bankruptcy (query below):
CREATE TABLE IF NOT EXISTS y_query AS
SELECT
A.dir_id, A.linked_dir_ir, A.bankrupt_co_id, A.co_name, A.event_date,
B.conn_co_id, B.conn_co_name, B.conn_co_type, B.conn_co_start_date, B.conn_co_end_date,
(CASE WHEN conn_co_start_date >= event_date THEN 1 ELSE 0 END) AS dir_hired
FROM (
SELECT
C.dir_id, C.linked_dir_ir, C.conn_co_id as bankrupt_co_id, C.overlap_start_date, C.overlap_end_date, C.conn_co_type,
D.co_name, D.filing_date, D.event_date
FROM director_network C
INNER JOIN company_bankruptcy D
ON C.conn_co_id = D.co_id
WHERE (
(C.overlap_end_date >= DATE(D.event_date, '-5 years')) AND
(C.overlap_end_date <= D.event_date))
) A
LEFT OUTER JOIN company_network B
ON A.dir_id = B.dir_id;
(linked_dir_ir should read linked_dir_id but I'm on a slow computer and it would take ~1hr to change the column name)
So, that table is fine, the query takes a while to run but it works as intended. But now I need to count the number of directors (linked_dir_ir) a given director (dir_id) was associated (ie, each row is a connection) for each bankrupt company (bankrupt_co_id) the director was in, and each connected company (conn_co_id). There can be many lines connecting a pair of directors because a new entry is made if any of them receives a promotion and so on.
(A few rows of y_query table: y_query)
So, I thought this query would work, but I'm running into problems:
SELECT dir_id, bankrupt_co_id, conn_co_id, COUNT(DISTINCT linked_dir_ir) as conn_dirs
FROM y_query
WHERE bankrupt_co_id != conn_co_id
GROUP BY dir_id, bankrupt_co_id, conn_co_id;
I am not sure why but this query disregards the last group (conn_co_id) and the result is the same for any dir_id and bankrupt_co_id, where it should vary also based on what conn_co_id is. A sample of the result (it only changes when dir_id or bankrupt_co_id changes, as depicted):
resulting query
The result is the same as if I'd only grouped with dir_id and bankrupt_co_id, when it should be different for each conn_co_id. I've done a lot of research into GROUP BY statements and how it can be tricky, but I haven't been able to crack this one. I'd greatly appreciate any help on this!
Its hard to reproduce your result. But your query with multiple groupings seems to be fine. See example below:
CREATE TABLE test (dir_id INTEGER, bankrupt_co_id INTEGER, conn_co_id INTEGER, linked_dir_id INTEGER);
with some dummy data:
select * from test;
dir_id bankrupt_co_id conn_co_id linked_dir_id
---------- -------------- ---------- -------------
1 1 1 1
1 1 1 2
1 1 1 4
1 1 1 5
1 1 1 6
1 1 1 7
1 1 2 1
1 1 2 2
1 1 2 3
1 2 2 1
3 3 2 1
3 1 2 1
3 2 2 1
3 2 2 4
1 1 1 3
1 1 4 4
The query including your conn_co_id results in:
SELECT dir_id, bankrupt_co_id, conn_co_id, COUNT(DISTINCT linked_dir_id) as conn_dirs FROM test WHERE bankrupt_co_id!=conn_co_id GROUP BY dir_id, bankrupt_co_id, conn_co_id;
dir_id bankrupt_co_id conn_co_id conn_dirs
---------- -------------- ---------- ----------
1 1 2 3
1 1 4 1
3 1 2 1
3 3 2 1
whereas without the top two results are combined:
SELECT dir_id, bankrupt_co_id, conn_co_id, COUNT(DISTINCT linked_dir_id) as conn_dirs FROM test WHERE bankrupt_co_id!=conn_co_id GROUP BY dir_id, bankrupt_co_id;
dir_id bankrupt_co_id conn_co_id conn_dirs
---------- -------------- ---------- ----------
1 1 4 4
3 1 2 1
3 3 2 1

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