Combination of group by, order and distinct - sql

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
)

Related

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.

assign a new value based on combinations

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

SQL for set of all in group by

Assume I have table foo
A B C
==============
1 1 1
1 2 3
1 2 4
1 3 6
2 2 6
I want the set of all C where I have a duplicate AB. Something like:
select all(C) from foo group by a, b having count(b) > 1
I want the result to be
all(C)
===
3
4
Is there an easy way to do this in Oracle SQL?
SELECT t1.c
FROM foo t1
JOIN foo t2 ON (t1.a = t2.a AND
t1.b = t2.b AND
t1.rowid != t2.rowid)
should give you what you're after. A bit more efficient would likely be to use an analytic function
SELECT c
FROM (SELECT f.*,
count(*) over (partition by a, b) cnt
FROM foo f)
WHERE cnt > 1
Try this:
SELECT C
FROM
(select C, COUNT(*) OVER(PARTITION BY A, B) AS DUPLICATES
from MY_TABLE) AS RESULTS
WHERE DUPLICATES > 1
I figured it out. I used
select f.c from foo f
join (select a, b from foo group by a, b having count(c) > 1) dupes
on dupes.a = f.a and dupes.b = f.b;

"Cluster" Code Help in SQL

I am relative newcomer to SQL, but have gained many useful ideas through the site. Now I'm stuck on a piece of code that seems simple enough, but for some reason I can't wrap my head around it.
I am trying to create a third column (Column Z) based off of the first two columns below:
Column X Column Y
-------------------
1 a
1 b
1 c
2 a
2 d
2 e
2 f
4 b
5 i
5 c
3 g
3 h
6 j
6 k
6 l
What i need to have happen in Column Z:
For each individual value found in Column Y, note the value of Column X
Likewise, for each individual value in Column X, note the value of Column Y
Then, cluster (RANK/ROW_NUMBER?) these into groups seen below:
Column X Column Y Column Z
-----------------------------
1 a 1
1 b 1
1 c 1
2 a 1
2 d 1
2 e 1
2 f 1
4 b 1
5 i 1
5 c 1
3 g 2
3 h 2
6 j 3
6 k 3
6 l 3
I hope I've been clear enough without over-complicating things. My head has been spinning all morning. Let me know if anyone needs any more info.
Greatly appreciated in advance!
I have faced exactly this problem for some analyses in the past. The only way I could get it to work is by doing a loop, that incrementally adds in the information.
The loop assigns the minimum "x" value within each group as the group id. By your rules, this is guaranteed to be unique. It starts by assigning the current x value to z. It then finds the minimum z along the x and y dimensions. It repeats this process until no records change.
Given your data, the following is an outline of how to do it:
update t set z = x
while 1=1
begin
with toupdate as (
select t.*,
min(z) over (partition by x) as idx,
min(z) over (partition by y) as idy from t
)
update toupdate
set z = (case when idx < idy then idx else idy end)
where z > idx or z > idy;
if (##ROWCOUNT = 0) break;
end;
;with a as
(
select z, dense_rank() over (order by z) newZ from t
)
update a set z = newZ
Maybe not the best way, but it works
SQLFiddle http://sqlfiddle.com/#!3/99532/1
;WITH cte AS (
SELECT *, ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS row_nb
FROM #t
)
, c2 AS (
SELECT e1.*
,CASE WHEN EXISTS(SELECT * FROM cte e2 WHERE e1.Y = e2.Y and e2.row_nb < e1.row_nb) THEN 1 ELSE 0 END as ex
FROM cte e1
)
, c3 AS (
SELECT X,1 - SIGN(SUM(ex)) as ex,MAX(row_nb) as max_row_nb
FROM c2
GROUP BY X
)
SELECT
cte.X,cte.Y
,(SELECT SUM(cc3.ex) FROM c3 cc3 where cc3.max_row_nb<= c3.max_row_nb) AS Z
FROM cte
INNER JOIN c3
ON c3.X = cte.X
ORDER BY cte.row_nb
declare #t table (x tinyint, y char(1), z tinyint)
insert #t (x,y) values(1,'a'),(1,'b'),(1,'c'),(2,'a'),(2,'d'),(2,'e'),(2,'c'),
(2,'f'),(4,'b'),(5,'i'),(5,'c'),(3,'g'),(3,'h'),(6,'j'),(6,'k'),(6,'l'),(7,'v')
;with a as
(
select x,parent from
(
select x, min(x) over (partition by y) parent from #t
) a
where x > parent
), b as
(
select x, parent from a
union all
select a.x, b.parent
from a join b on a.parent = b.x
), c as
(
select x, min(parent) parent
from b
group by x
), d as
(
select t.x,t.y, t.z,
dense_rank() over (order by coalesce(c.parent, t.x)) calculatedZ
from #t t
left join c on t.x = c.x
)
select x,y,calculatedZ as z from d
-- if you want to update instead of selecting, replace last line with:
-- update d set z = newz
-- select x,y,z from #t
option (maxrecursion 0)
Result:
x y z
1 a 1
1 b 1
1 c 1
2 a 1
2 d 1
2 e 1
2 c 1
2 f 1
4 b 1
5 i 1
5 c 1
3 g 2
3 h 2
6 j 3
6 k 3
6 l 3
8 j 3
7 v 4

SQL grouping

I have a table with the following columns:
A B C
---------
1 10 X
1 11 X
2 15 X
3 20 Y
4 15 Y
4 20 Y
I want to group the data based on the B and C columns and count the distinct values of the A column. But if there are two ore more rows where the value on the A column is the same I want to get the maximum value from the B column.
If I do a simple group by the result would be:
B C Count
--------------
10 X 1
11 X 1
15 X 1
20 Y 2
15 Y 1
What I want is this result:
B C Count
--------------
11 X 1
15 X 1
20 Y 2
Is there any query that can return this result. Server is SQL Server 2005.
I like to work in steps: first get rid of duplicate A records, then group. Not the most efficient, but it works on your example.
with t1 as (
select A, max(B) as B, C
from YourTable
group by A, C
)
select count(A) as CountA, B, C
from t1
group by B, C
I have actually tested this:
SELECT
MAX( B ) AS B,
C,
Count
FROM
(
SELECT
B, C, COUNT(DISTINCT A) AS Count
FROM
t
GROUP BY
B, C
) X
GROUP BY C, Count
and it gives me:
B C Count
---- ---- --------
15 X 1
15 y 1
20 y 2
WITH cteA AS
(
SELECT
A, C,
MAX(B) OVER(PARTITION BY A, C) [Max]
FROM T1
)
SELECT
[Max] AS B, C,
COUNT(DISTINCT A) AS [Count]
FROM cteA
GROUP BY C, [Max];
Check this out. This should work in Oracle, although I haven't tested it;
select count(a), BB, CC from
(
select a, max(B) BB, Max(C) CC
from yourtable
group by a
)
group by BB,CC