I have a table with about 20 columns and 2000 rows.
Example:
Col1 Col2 Col3 Col4 ...
A01 22 AB 11
A01 22 AX 112
A01 23 A5 11
A02 20 AB AA
A04 21 AB 11
A04 21 AU 11
A04 29 AB BA
A05 21 AB 11
AAA 111 XX 18
AAA 222 GT 1O
...
I need a select which displays all rows and all columns that satisfy the requirement of two columns (Col1 and Col2) based on the following:
if Col1 is unique - show row,
or
if Col1 is not unique show all row only if Col1 and Col2 are same.
From the previos table is after select the result:
Col1 Col2 Col3 Col4 ...
A01 22 AB 11
A01 22 AX 112
A02 20 AB AA
A04 21 AB 11
A04 21 AU 11
A05 21 AB 11
AAA 111 XX 18
...
The new table (your solution) contains data:
Col1 Col2 Col3 Col4 ...
A01 22 AB 11
A01 22 AX 112
A02 20 AB AA
A04 21 AB 11
A04 21 AU 11
A05 21 AB 11
AAA 111 XX 18
...
what I wont see from this is:
Col1 Col2 Col3 Col4 ...
A01 2 AB 11
A02 1 AB AA
A04 2 AB 11
A05 1 AB 11
AAA 1 XX 18
...
In Oracle and MS SQL I would use analytical functions:
select * from
(
select
t.* ,
count(Col1) over (partition by Col1) as count_col1,
count(Col2) over (partition by Col1, Col2) as count_col2
from yourTable t
) t
where count_col1 = 1 or count_col2 > 1;
See this fiddle (Oracle) and this fiddle (MSSQL) as proof.
select *
from table t1
join (select col1
from table
group by col1
having avg(col2)=max(col2)) t2
on t1.col1=t2.col1
Seeing that I didn't look at your example .. and your request is slightly different then the example, Because my query checks that for a col1 all col2 should be the same. It will not display the ones that are the same.
In this case the answer will be
select *
from table1 t1
join (select col1,col2
from table1
group by col1,col2
having count(*)>1
union
select col1,cast(null as varchar)
from table1 group by col1
having count(*)=1) t2
on t1.col1=t2.col1 and t1.col2=isnull(t2.col2,t1.col2)
This is the updated query, and the fiddle for it http://sqlfiddle.com/#!3/e944b/2/0
Ok .. updated one more time:
select *
from table1 t1
join (select col1,col2
from table1
group by col1,col2
having count(*)>1
union
select col1,min(col2)
from table1 group by col1
having count(*)=1 or count(*)=count(distinct col2)) t2
on t1.col1=t2.col1 and t1.col2=t2.col2
and with fiddle http://sqlfiddle.com/#!3/d5437/12/0
This should be enough for the second problem:
select t3.*
from (select distinct col1 from table1)t1
cross apply (select top 1 * from table1 t2 where t1.col1=t2.col1) t3
and the fiddle: http://sqlfiddle.com/#!3/e944b/4/0
Related
I need to find the sum,avg,max and min of a column:
Table_ab
col1 | col2 | col3
ab |10 am | {10,20,30}
ab |10.15am | {20,30,40}
Expected result
col1 | col2 | col3 | sum_col3 | avg_col3 | max_col3 | min_col3
ab |10 am |{10,20,30} |60 |20 |30 | 10
Not the best data model, as you've already been told. Anyway: one (usual) option is to split values in col3 into rows to perform those calculations. Here's how (sample data in lines #1 - 4; query begins at line #6):
SQL> with table_ab (col1, col2, col3) as
2 (select 'ab', '10 am' , '10,20,30' from dual union all
3 select 'ab', '10.15am', '20,30,40' from dual
4 ),
5 -- split col3 into rows
6 temp as
7 (select col1,
8 col2,
9 col3,
10 to_number(regexp_substr(col3, '[^,]+', 1, column_value)) val
11 from table_ab cross join
12 table(cast(multiset(select level from dual
13 connect by level <= regexp_count(col3, ',') + 1
14 ) as sys.odcinumberlist))
15 )
16 -- aggregate!
17 select col1,
18 col2,
19 col3,
20 sum(val) sum_col3,
21 avg(val) avg_col3,
22 max(val) max_col3,
23 min(val) min_col3
24 from temp
25 group by col1, col2, col3;
CO COL2 COL3 SUM_COL3 AVG_COL3 MAX_COL3 MIN_COL3
-- ------- -------- ---------- ---------- ---------- ----------
ab 10.15am 20,30,40 90 30 40 20
ab 10 am 10,20,30 60 20 30 10
SQL>
table
ID
col1
coll2
1
104A
103A
2
301A
300A
3
103A
101A
4
102A
101A
5
201A
200A
6
101A
100A
If i give col1 = 104A in the where condition,output shoud be like
ID1
col1
col2
1
104A
103A
3
103A
101A
6
101A
100A
col2 should match with the next selected row col1
You must Recursive produce the desired output
WITH cte AS (
SELECT *
FROM tbl t
WHERE t.col1 = '104A'
UNION ALL
SELECT t.*
FROM tbl t join cte c on t.col1 = c.coll2
)
SELECT * FROM cte
demo in db<>fiddle
This is source table
Id. A B
---------------
1 aa bb
2 cc dd
The output table need is
Id. Col1 Col2
------------------------
1 A aa
1 B bb
2 A cc
2 B dd
You can use union all:
select id, 'A' as col1, a as col2 from t
union all
select id, 'B', b from t;
I bring forth an interesting problem that has been bothering me for the past few days. Let's say you have the following data structure:
Col1 | Col2 | Col3 | Col4
100 | "Val1" | 0 | 100
100 | "Val2" | 1 | null
100 | "Val 3" | 0 | null
101 | "Val4" | 0 | null
101 | "Val5" | 1 | null
102 | "Val6" | 0 | null
I need that one row where Col4!=null. If all rows' Col4 is null then return me a row where Col3=1, but if both Col4 is null and Col3=0, then return me any one row.
So the result set for the above data will look like,
Col1 | Col2 | Col3 | Col4
100 | "Val1" | 0 | 100
101 | "Val5" | 1 | null
102 | "Val6" | 0 | null
I know this could be done using analytics function, order them by Col1, Col4 and Col3 and use an analytic function to get the first row in each group but we are using our inhouse ORM that doesn't support analytic function.
Please let me know if this can be done using simple SQL (JOIN, Case, etc).
Edit:
There will only be one row per group where Col4 has non-null value and one row per group where col3 is 1. Also, a single row in the group can satisfy both conditions of having Col4 not null and Col3=1.
How about this? Every CONDx CTE solves one condition.
COND1 returns rows whose COL4 is not null
COND2 returns rows whose COL1 doesn't exist in COND1 result set and has NULLs for COL4 (in that case, count of distinct values = 0) and COL3 = 1
COND3 is everything that's left
The final result is union of all those.
SQL> with test (col1, col2, col3, col4) as
2 (select 100, 'val1', 0, 100 from dual union all
3 select 100, 'val2', 1, null from dual union all
4 select 100, 'val3', 0, null from dual union all
5 select 101, 'val4', 0, null from dual union all
6 select 101, 'val5', 1, null from dual union all
7 select 102, 'val6', 0, null from dual
8 ),
9 cond1 as
10 (select col1, col2, col3, col4
11 From test
12 where col4 is not null
13 ),
14 cond2 as
15 (select col1, col2, col3, col4
16 from test t
17 where t.col1 not in (select col1 from cond1)
18 and col1 in (select col1
19 from test
20 group by col1
21 having count(distinct col4) = 0
22 )
23 and col3 = 1
24 ),
25 cond3 as
26 (select col1, col2, col3, col4
27 from test t
28 where t.col1 not in (select col1 from cond1
29 union all
30 select col1 from cond2
31 )
32 )
33 select col1, col2, col3, col4 from cond1
34 union all
35 select col1, col2, col3, col4 from cond2
36 union all
37 select col1, col2, col3, col4 from cond3
38 order by col1;
COL1 COL2 COL3 COL4
---------- ---- ---------- ----------
100 val1 0 100
101 val5 1
102 val6 0
SQL>
Let's say I have the dataset that looks like:
col1 col2 col3
a 2 20
a 3 12
a 4 34
b 2 44
c 3 23
c 5 13
....
What I want is a count of col1.
Output:
col1 col2 col3 count
a 2 20 3
a 3 12 3
a 4 34 3
b 2 44 1
c 3 23 2
c 5 13 2
.......
I know I can do by:
with cte as (
select col1, count(*) count
from tab1)
select a.col1,a.col2,a.col3,cte.count
from tab1
join cte on a.col1=cte.col1
But is there any other I can do that without cross apply or cte?
Also, assuming there are more than 3 letters in col1, so I couldn't use sum function either:
SUM(CASE WHEN ItemID = 'a' THEN 1 ELSE 0 END) AS count_a
If you're using SQL Server 2008+, you can use COUNT() OVER():
SELECT *,
COUNT(*) OVER(PARTITION BY col1)
FROM tab1
ONLINE DEMO