Update with group by - oracle

I'm stumped on what seemed to be a simple UPDATE statement.
I'm looking for an UPDATE that uses two values. The first (a) is used to group, the second (b) is used to find a local minimum of values within the respective group. As a little extra there is a threshold value on b: Any value 1 or smaller shall remain as it is.
drop table t1;
create table t1 (a number, b number);
insert into t1 values (1,0);
insert into t1 values (1,1);
insert into t1 values (2,1);
insert into t1 values (2,2);
insert into t1 values (3,1);
insert into t1 values (3,2);
insert into t1 values (3,3);
insert into t1 values (4,1);
insert into t1 values (4,3);
insert into t1 values (4,4);
insert into t1 values (4,5);
-- 1,0 -> 1,0
-- 1,1 -> 1,1
-- 2,1 -> 2,1
-- 2,2 -> 2,2
-- 3,1 -> 3,1
-- 3,2 -> 3,2
-- 3,3 -> 3,2 <-
-- 4,1 -> 4,1
-- 4,3 -> 4,3 <-
-- 4,4 -> 4,3 <-
-- 4,5 -> 4,3 <-
Obviously not sufficient is:
update t1 x
set b = (select min(b) from t1 where b > 1)
;
Whatever more complicated stuff I try, e.g.
UPDATE t1 x
set (a,b) = (select distinct a,b from (
select a, min(b) from t1 where b > 1 group by a)
)
;
I get
SQL-Fehler: ORA-01427: Unterabfrage für eine Zeile liefert mehr als eine Zeile
01427. 00000 - "single-row subquery returns more than one row"
which is not overly surprising as I need a row for each value of a.
Of course I could write a PL/SQL Procedure with a cursor loop but is it possible in a single elegant SQL statement? Maybe using partition by?

Your question is a bit confusing.
You say that you would like to set value b to a minimum value from partition a that column b is in row with, while the rows containing b = 1 should remain untouched.
From what I can see in your question as comments (I assume it's your expected output) you also want to get the minimum value that follows 1 within a partition - so you basically want the minimum value of b that is greater than 1.
Below is SQL query that does this
UPDATE t1 alias
SET b = (
SELECT min(b)
FROM t1
WHERE alias.a = t1.a
AND t1.b > 1 -- this would get the minimum value higher than 1
GROUP BY a
)
WHERE alias.b > 1 -- update will not affect rows with b <= 1
Output after update
a | b
---+---
1 | 0
1 | 1
2 | 1
2 | 2
3 | 1
3 | 2
3 | 2
4 | 1
4 | 3
4 | 3
4 | 3

Related

How to use comma separated value from column to IN clause [duplicate]

This question already has answers here:
Using the "IN" clause with a comma delimited string from the output of a replace() function in Oracle SQL
(6 answers)
Closed 6 years ago.
I have a table, which contains below values(small snap of data):
Row#| Column A | Column B
--------------------------
1 |A | A,B
2 |C | A,D
3 |M | Z
4 |C |A,B,L
5 |F |F
6 |C |O
List goes on and on. Column A and B is populated by other application, so its combinations are not predictable. Table has more than 4000 rows.
My search values are not fixed; I have to use search value from Column B of same row for which I am looking for data in Column A.
I want to check Column A's value is IN Column B or not. Basically compare Column A with B, if find match in B result should skip that row.
In above case Row#1's Column A's value is in B which is not true for row#2.
My query should reach all rows and compare Column A with B. Query should return Row#1 and 5
This question is different Using the "IN" clause with a comma delimited string from the output of a replace() function in Oracle SQL
As it has fixed set of search value
select * from my_table where instr(columnB, columnA) > 0;
Example: Should contain
select * from (
select 1 a, 'A' b, 'A,B' c from dual union all
select 2, 'C', 'A,D' from dual ) t
where instr(c,b) > 0;
Result:
A B C
-----------
1 A A,B
Example 2: Shouldn't contain
select * from (
select 1 a, 'A' b, 'A,B' c from dual union all
select 2, 'C', 'A,D' from dual ) t
where instr(c,b) = 0;
Result:
A B C
-----------
2 C A,D

How to speed up EDIT_DISTANCE and Insert Query?

---------------
MASTER TABLE
---------------
DATA_KEY NUMBER
TEXT VARCHAR2(2000)
ORDER_NO NUMBER
---------------
DETAIL TABLE
---------------
DATA_KEY NUMBER
SIMILAR_DATA_KEY NUMBER
DISTANCE_COUNT NUMBER
---------------
INSERT QUERY
---------------
INSERT INTO DETAIL
(
SELECT DATA_KEY, SIMILAR_DATA_KEY, DISTANCE_COUNT
FROM
(
SELECT A.DATA_KEY AS DATA_KEY, B.DATA_KEY AS SIMILAR_DATA_KEY,
UTL_MATCH.EDIT_DISTANCE(A.TEXT, B.TEXT) AS DISTANCE_COUNT
FROM
(SELECT DATA_KEY, TEXT, ORDER_NO FROM MASTER) A
INNER JOIN
(SELECT DATA_KEY, TEXT, ORDER_NO FROM MASTER) B
ON (A.ORDER_NO < B.ORDER_NO)
)
WHERE DISTANCE_COUNT <= 5
)
I need compare MASTER table TEXT field with other TEXT field.
indexes are not exist.
master table 90,000 rows.
ORDER_NO field is for avoid duplicated compare. (1 .. 90000)
=============================================================
A.ORDER_NO < B.ORDER_NO
------------------------------------------
1, 1 <- exclude
1, 2 <- join
1, 3 <- join
1, 4 <- join
..
2, 1 <- exclude
2, 2 <- exclude
2, 3 <- join
2, 4 <- join
...
3, 1 <- exclude
3, 2 <- exclude
3, 3 <- exclude
3, 4 <- join
1. NOT need compare 1 and 1
2. Need compare 1 and 2
3. NOT need compare 2 and 1 (because, duplicate 2.)
so, for decrease compare count...
=============================================================
Slow zone is (WHERE DISTANCE_COUNT <= 5) ?
Slow zone is comparing rows (90000*89999/2) ?
Query elapse time is 7 days.
6,000 rows inserted to DETAIL table.
How to speed up?
I'm sorry for poor English...

Inserting Data using join in Hive

Hi I have two Tables and after Join I want to Insert Data into Third Tables. Problem I am facing is I have to create multiple records based on the value of Join.
Table 1
A B
-------
1 X
2 y
3 x
Table 2
A C
-------
1 Y
2 N
3 Y
I need to join Table 1 and Table 2 on Column A and Based on value of Column C in Table 2 I need to insert Records in Table 3
Rule
If Column C value is 'Y' then insert 3 Records as 'Red','Green','Blue'
If Column C value is 'N then insert 2 records as 'White','Black'
So Result should be
Table 3
A D
-----------
1 Red
1 Green
1 Blue
2 White
2 Black
3 Red
3 Green
3 Blue
Can you let me know how to achieve this using hiveql ? Thanks
You can create a third table Color
Table Color
------------------
Flag Color
Y Red
Y Blue
Y Green
N White
N Black
Now you can join them easily
Select * from Table1 T1
JOIN Table2 T2
ON T1.A = T2.C
JOIN COLOR C
ON T2.C = C.Flag

Select an included values

I'm using Oracle SQL and i need help with a query. Hope it's not too much easy one. I did't find an answer for it in Google.
I have a table that need to be aggregated by ID column and then to select only the records that two values are included in a certain table (and both of them).
Table for example
ID | Value
1 | Y
1 | N
2 | N
2 | N
2 | Y
3 | Y
3 | Y
4 | Y
5 | Y
5 | N
5 | Y
5 | N
The output table need to include only the IDs that both Y and N are included in Value table. Output:
ID
1
2
5
Another solution that groups by the ID and uses HAVING to return only those with > 1 DISTINCT values:
with v_data(id, value) as (
select 1, 'Y' from dual union all
select 1, 'N' from dual union all
select 2, 'Y' from dual)
select id
from v_data
group by id
having count(distinct value) > 1
select distinct a.id
from your_table a inner join your_table b on a.id = b.id and a.value != b.value;

Fix numbering of many item groups in table

I have a table with these columns
create table demo (
ID integer not null,
INDEX integer not null,
DATA varchar2(10)
)
Example data:
ID INDEX DATA
1 1 A
1 3 B
2 1 C
2 1 D
2 5 E
I want:
ID INDEX DATA
1 1 A
1 2 B -- 3 -> 2
2 1 C or D -- 1 -> 1 or 2
2 2 D or C -- 1 -> 2 or 1
2 3 E -- 5 -> 3 (closing the gap)
Is there a way to renumber the INDEX per ID using a single SQL update? Note that the order of items should be preserved (i.e. the item "E" should still be after the items "C" and "D" but the order of "C" and "D" doesn't really matter.
The goal is to be able to create a primary key over ID and INDEX.
If that matters, I'm on Oracle 10g.
MERGE
INTO demo d
USING (
SELECT rowid AS rid, ROW_NUMBER() OVER (PARTITION BY id ORDER BY index) AS rn
FROM demo
) d2
ON (d.rowid = d2.rid)
WHEN MATCHED THEN
UPDATE
SET index = rn

Resources