assign a new value based on combinations - sql

I have these two tables
the first one has id's and a categorical variable 'code'
table1
id code
1 1 F
2 1 B
3 1 J
4 2 D
5 2 B
6 2 F
7 2 G
8 2 C
9 2 D
10 3 G
11 3 G
12 3 G
13 4 B
14 4 F
15 4 C
16 4 D
17 5 C
18 5 A
19 5 G
20 5 D
and table2
has some combinations of the categorical variable 'code' which are assigned a new category 'code3'
code1 code2 code_3
1 C B O
2 B A K
3 A C L
4 E B N
5 A D J
6 D B L
id's in table1 come with multiple codes, the combinations of those codes result in new codes found on table2.
how to I assign the id's in table1 the values in table2 code3 based on the combinations they have?
desired output
would be something like
id code
1 1 F
2 1 B
3 1 J
5 2 L -- added, while a B and D removed
6 2 F
7 2 G
8 2 C
...

You can get the list of new codes to add by doing a self-join and then joining to table2 to find matches:
select t1.id, t2.code3
from table1 t1 join
table1 tt1
on t1.id = tt1.id and
t1.code < t2.code join
table2 t2
on t2.code1 = t1.code and
t2.code2 = tt1.code;

SELECT id, code, NVL (code3, code)
FROM (SELECT id,
code,
hh,
rr,
gg,
code3
FROM ( SELECT id,
code,
hh,
code || hh rr
FROM --here rr is used as foreign key which refer gg ,which can used as primary key of table2
(SELECT id,
code,
LEAD (code, 1, code)
OVER (PARTITION BY id ORDER BY ROWNUM)
hh
FROM table1)
ORDER BY code, hh) e, --hh gives the code of next row of each code of table1
( SELECT code1 || code2 gg, code3
FROM table2
ORDER BY code1, code2) b
WHERE e.rr = b.gg(+))
ORDER BY id; --here left outer join is used to get desired output
-- ORDER BY code,hh and ORDER BY code1,code2 are used to make sure that SUM(D+B)=L AND SUM(B+D)=L

Related

Select distinct fields from multiple table

Table 1 -
ID VehicleID
1 A
2 A
3 A
1 B
1 C
4 C
2 D
Table 2-
ID VehicleID VehicleNo
1 A AA
2 A AA
3 A
1 B BB
1 C CC
4 C CC
2 D DD
Output-
VehicleId VehicleNo
A AA
B BB
C CC
D DD
This is how I understood it; read comments within code.
SQL> with
2 -- calculate "RN" (so that you'd have something to match rows on)
3 a as
4 (select vehicleid,
5 row_number() over (order by vehicleid) rn
6 from (select distinct vehicleid from tab1)
7 ),
8 b as
9 (select vehicleno,
10 row_number() over (order by vehicleno) rn
11 from (select distinct vehicleno from tab2)
12 )
13 -- final query
14 select a.vehicleid, b.vehicleno
15 from a left join b on a.rn = b.rn;
VEHICLEID VEHICLENO
---------- ----------
A AA
B BB
C CC
D DD
SQL>
One simple method is aggregation:
select VehicleId, max(VehicleNo) as VehicleNo
from table2
group by VehicleId;

How can I Select 1st, 2nd and 3rd values in different columns - Ms Access

I have this data in a table:
ID ItemID ItemSupplier
1 1 E
2 2 E
3 2 B
4 3 B
5 4 B
6 4 E
7 4 C
8 5 'NULL'
9 6 C
I would like to write a Query to select it as such:
ItemID Supplier1 Supplier2 Supplier3
1 E
2 E B
3 B
4 B E C
5
6 C
But I can only get the first column with :
SELECT ItemID, FIRST(ItemSupplier) AS Supplier1
FROM myTable GROUP BY ItemID
Thank you
MS Access is not the best tool for this.
One method uses a correlated subquery to enumerate the values and then conditional aggregation:
select itemid,
max(iif(seqnum = 1, itemsupplier, null)) as supplier_1,
max(iif(seqnum = 2, itemsupplier, null)) as supplier_2,
max(iif(seqnum = 3, itemsupplier, null)) as supplier_3
from (select t.*,
(select count(*)
from t as t2
where t2.itemid = t.itemid and t2.id <= t.id
) as seqnum
from t
) as t
group by itemid;
Almost any other database supports window functions, which would make this much more efficient.

SQL Select group where some attribute is same

Lets say I have a table like this
A B C
-----
1 a 12
2 a 23
3 b 43
4 c 25
5 c 44
6 d 34
How to select only rows where B exists in another row?
Result would be:
A B C
-----
1 a 12
2 a 23
4 c 25
5 c 44
I'm not sure what you are expecting but eliminating B & D
we can achieve like this
Select T.A,T.B,T.C from Table T
INNER JOIN (
SELECT B FROM Table
groUP by b
having count(B) > 1 )TT
ON T.B = TT.B
Just use exists:
select t.*
from t
where exists (select 1
from t t2
where t2.b = t.b and t2.a <> t.a
);
With an index on t(b, a), this is likely to be the fastest method.

Can SQL interleave data based on a range limit?

Consider the following data
Table 1
Key Value
--- -----
A 1
B 2
C 3
D 4
E 5
F 6
G 7
H 8
I 9
J 10
Table 2
Q MaxValue
- --------
X 3
Y 6
Z 10
I'm trying to create a join that matches table 1 with table 2 when the values of table 1 are less than or equal to MaxValue in table 2, but only where they are greater than the prior MaxValue, like so
Result
Key Value Q MaxValue
--- ----- - --------
A 1 X 3
B 2 X 3
C 3 X 3
D 4 Y 6
E 5 Y 6
F 6 Y 6
G 7 Z 10
H 8 Z 10
I 9 Z 10
J 10 Z 10
Here you see that while A-F all meet the criteria of being less than or equal to 6 (Y's MaxValue), I want A-C to be matched only to X since they already match X's criteria of being less than 3, X being the 'prior max value'.
So can this be achieved in SQL?
This isn't very pretty but it should work for you:
select Z.[Key], Z.Value, T2.Q, Z.MaxValue from
(
select Y.[Key], Y.Value, Min(MaxValue) as MaxValue from
(
select T1.*, T2.MaxValue from Table1 T1 cross join Table2 T2
) Y
where Y.Value <= Y.MaxValue
group by Y.[Key], Y.Value
) Z
inner join Table2 T2 on Z.MaxValue = T2.MaxValue
select T1.Value, T2.MaxValue from Table1 T1 cross join Table2 T2 gets all possible combinations of Value and MaxValue.
Then I'm grouping it based on Value and Key and including the condition where Y.Value <= Y.MaxValue to exclude combinations where there are overlapping MaxValues for the same Value.
Finally, I'm getting the original Q column from Table2.
Hopefully help
select [Key],
[Value],
(SELECT [Q] FROM [TB2] T2 WHERE [MaxValue] IN (SELECT MIN(MaxValue) FROM [Table1] T2 WHERE T1.[Value]<= T2.[MaxValue])) AS Q,
(SELECT MIN(MaxValue) FROM [TB2] T2 WHERE T1.[Value]<= T2.[MaxValue]) AS MaxValue
FROM [Table2] T1

Combination of group by, order and distinct

My query
SELECT a, b, c
FROM table
WHERE
a > 0 AND a < 4 AND
b IN (
SELECT z FROM table2
WHERE x = y
)
produces the following output:
A B C
1 1 Car
1 1 Keyboard
1 2 Apple
1 3 Frog
2 1 Carrot
2 2 Parrot
3 1 Doll
what I want is the following output
A B C
1 1 Car
2 1 Carrot
3 1 Doll
So basically for every A, the lowest B and associated C (as well as other columns).
I tried various join types, group bys, but I am running out of ideas.
How can I accomplish this?
Use a Top N Apply
SELECT a, b, c
FROM table
CROSS APPLY (SELECT top 1 z
FROM table2
WHERE x = y
order by z ) t2
WHERE a > 0 AND a < 4 AND
Do a join on a subquery:
SELECT a, b, c
FROM table t1
INNER JOIN (SELECT a a2, MIN(b) b2 FROM table GROUP BY a) t2
ON t1.a = t2.a2 AND t1.b = t2.b2
WHERE
a > 0 AND a < 4 AND
b IN (
SELECT z FROM table2
WHERE x = y
)