How to do aggregation with calculation in SQL - sql

I have similar table with two columns, C2 will have multiple values . I need the output as
Condition for t2.c2 is If all the values in t1.C2 are <= 5, then 1 else 0, please advice what would be the best logic.

Another option
select distinct C1,
if(logical_and(C2 <= 5) over(partition by C1), 1, 0) as C2
from your_table
if applied to sample data in your question - output is

This will give desired result. First section has t1 table data, second section has t2 data based on logic requested.
with t1 as
(select 'A10' as C1, 2 as C2 union all
select 'A10' as C1, 3 as C2 union all
select 'A10' as C1, 4 as C2 union all
select 'A10' as C1, 5 as C2 union all
select 'A10' as C1, 3 as C2 union all
select 'A10' as C1, 4 as C2 union all
select 'A10' as C1, 2 as C2 union all
select 'A10' as C1, 4 as C2 union all
select 'A10' as C1, 5 as C2 union all
select 'A10' as C1, 3 as C2 )
select C1 , if(MAX(C2) <=5,1,0) as C2
from t1 group by C1;

Related

postgresql - count distinct combination of three columns- order doesn't matter

I'm trying to count distinct combinations of three columns, order of the columns doesn't matter
sample :
a a a
a a b
a b a
b b a
b a b
the result I'm getting :
a a a 1
a a b 1
a b a 1
b b a 1
b a b 1
desired result
aaa 1
aab 2
bba 2
You can use an ordered array
select v[1], v[2], v[3], count(*) n
from tbl t
cross join lateral (
select array_agg(col order by col) v
from (
values (c1),(c2),(c3)
) t(col)
) s
group by v[1], v[2], v[3];
db<>fiddle
Maybe you can use checksums for getting the required result eg if it is really just combinations 'a' and 'b' that you are dealing with, you could convert the letters to integers (by calling the ASCII() function) and add these up so that you get a checksum.
TABLE
create table t (c1, c2, c3 ) as
select 'a', 'a', 'a' union all
select 'a', 'a', 'b' union all
select 'a', 'b', 'a' union all
select 'b', 'b', 'a' union all
select 'b', 'a', 'b' ;
Checksums
select c1, c2, c3, ascii( c1 ) + ascii( c2 ) + ascii( c3 ) as checksum
from t ;
-- output
c1 c2 c3 checksum
a a a 291
a a b 292
a b a 292
b b a 293
b a b 293
If this works for you, then you can use window functions eg
select c1, c2, c3, rc_ as rowcount
from (
select c1, c2, c3
, count(*) over ( partition by ascii( c1 ) + ascii( c2 ) + ascii( c3 ) order by 1 ) rc_
, row_number() over ( partition by ascii( c1 ) + ascii( c2 ) + ascii( c3 ) order by 1 ) rn_
from t
) sq
where rc_ = rn_ ;
-- output
c1 c2 c3 rowcount
a a a 1
a b a 2
b a b 2
See dbfiddle.
If you are dealing with strings that cannot easily converted to integers, you could create a mapping between the strings and integers, and implement the map_ as a view (so that it is easy to use in subsequent queries) eg
MAP
-- {1} find all distinct elements
-- {2} map each element to an integer
create view map_
as
select val_, rank() over ( order by val_ ) weight_
from (
select distinct val_
from (
select distinct c1 val_ from t union all
select distinct c2 from t union all
select distinct c3 from t
) all_elements
) unique_elements ;
Once you have this map, you can use its values for creating checksums (maybe also in a view) ...
Checksums
create view t_checksums_
as
select c1, c2, c3, c1weight + c2weight + c3weight as checksum
from (
select
c1, ( select weight_ from map_ where c1 = map_.val_ ) c1weight
, c2, ( select weight_ from map_ where c2 = map_.val_ ) c2weight
, c3, ( select weight_ from map_ where c3 = map_.val_ ) c3weight
from t
) valandweight ;
... and then, you can use the same query as before, for obtaining the final result - see dbfiddle.

Difference in two tables display difference in rows in single column

I have two tables which return 3 columns (Account #, Amount, Site Number)
Sample:
Table # 1
111111, 200, 14
111111,-200, 14
111111, 400, 15
111111, -400, 15
Table # 2
111111, 201, 14
111111,-200, 14
111111, 400, 15
111111, -400, 15
I am trying to graft a query that will no only show me the differences between the two tables like minus or where not exists but allow me pivot the data that is different from the one table to a column in the return
Something like this:
Act#: TblA Amount TblB Amount Site
111111, 200, 201, 14
When I use minus it simply gives me back the row in the top table that is different so if I did:
select * from TblA
MINUS
select * from TblB
Result:
111111, 200, 14
I know there must be way to do this and any help would be great!
Maybe the following queries will help. Principle: find the differences between the tables (contents of A minus contents of B, and vice versa),
then use these in a join, in order to "collapse" the result set. Using your test data (Oracle 12c):
(
select c1, c2, c3, 'in table#1' location from table#1
minus
select c1, c2, c3, 'in table#1' from table#2
)
union all
(
select c1, c2, c3, 'in table#2 (not in table#1)' from table#2
minus
select c1, c2, c3, 'in table#2 (not in table#1)' from table#1
);
-- result
C1 C2 C3 LOCATION
---------- ---------- ---------- ---------------------------
111111 200 14 in table#1
111111 201 14 in table#2 (not in table#1)
If there are no duplicates for the C1/C3 combinations, the following JOIN will give you the required result. (Maybe this will be "good enough" for your situation ...)
select
A.c1
--, B.c1
, A.c2
, B.c2
, A.c3
--, B.c3
from
(
select * from table#1
minus
select * from table#2
) A join (
select * from table#2
minus
select * from table#1
) B on A.c1 = B.c1 and A.c3 = B.c3
;
-- result
C1 C2 C2 C3
---------- ---------- ---------- ----------
111111 200 201 14
Dbfiddle here.
You could use FULL JOIN:
SELECT *
FROM tabA a
FULL JOIN tabB b
ON a.id = b.id -- here should be PK or UNIQUE col
WHERE NOT EXISTS (SELECT a.Account, a.Amount, a.Site_Number FROM dual
INTERSECT
SELECT b.Account, b.Amount, b.Site_Number FROM dual);
db<>fiddle demo

count of distinct columns in a record

How to count distinct columns values for a record
C1 C2 C3 C4
1 USER1 USER2 USER3
2 USER1 USER1 USER2
3 USER2 USER3 USER3
4 USER1 USER1 USER1
OUTPUT:
C1 COUNT
1 3
2 2
3 2
4 1
How to calculate distinct number of users in each record. Is there a way other than comparing column values.
Thanks.
One way is with unpivot:
select c1, count(distinct col) as cnt from (
select * from your_table
unpivot( col for lst in(C2, C3, C4) )
) tt
group by c1
For only three comparison columns this is the simplest thing which would work:
with cte as (
select c1, c2 from your_table
union
select c1, c3 from your_table
union
select c1, c4 from your_table
)
select c1, count(c2) as cnt
from cte
group by c1
/
The union operator produces a distinct set which feeds into the aggregated count.
with cte as (
select c1, c2 from your_table
union all
select c1, c3 from your_table
union all
select c1, c4 from your_table
)
select c1, count(c2) as cnt,count(distinct c2) as distinct_cnt
from cte
group by c1
/

SQL - find rows that have the same set of values in a column

I have a table and I want to select all rows that have exactly the same set of values that appear in a column and return them as a pair of a certain column.
For example, let's say I have a table named Table:
C1 C2
1 1
1 2
1 3
2 1
3 1
3 2
3 3
4 1
4 2
When I run my query, it should return the row:
1 3
because these are the two values in C1 that have the same set of values in column C2 (1,2,3).
I have an incorrect query below that returns all rows that have at least one matching value in C2 and I can't figure out how to correct it.
SELECT DISTINCT T1.C1, T2.C1
FROM Table T1, Table T2
WHERE T1.C1 != T2.C1
AND T1.C2 = T2.C2
AND T1.C1 < T2.C1
GROUP BY s1.suppId, s2.suppId;
Any help would be greatly appreciated, thanks.
As you do not specify, this one will handle the case of two sets of matching C2 values, outputting two rows - one for each matched set.
with thetable as (
SELECT 1 C1, 1 c2 from dual union
SELECT 1 C1, 2 c2 from dual union
SELECT 1 C1, 3 c2 from dual union
SELECT 2 C1, 1 c2 from dual union
SELECT 3 C1, 1 c2 from dual union
SELECT 3 C1, 2 c2 from dual union
SELECT 3 C1, 3 c2 from dual union
SELECT 4 C1, 1 c2 from dual union
SELECT 4 C1, 2 c2 from dual union
-- added fourrows to give a second set of matches
SELECT 5 C1, 1 c2 from dual union
SELECT 5 C1, 2 c2 from dual union
SELECT 6 C1, 1 c2 from dual union
SELECT 6 C1, 2 c2 from dual )
SELECT LIST_C2, List_c1
FROM (
SELECT list_c2
,LISTAGG(c1,',') WITHIN GROUP (ORDER BY c1) list_c1
FROM (
SELECT c1, LISTAGG(c2,',') WITHIN GROUP (ORDER BY c2) list_c2
FROM thetable
GROUP BY c1
)
group by list_c2
)
-- only bring back where we had more than one c1
WHERE instr(list_c1,',') != 0
Showing the following two sets of C2 values and their matching lists of C1s
LIST_C2 LIST_C1
"1,2" "4,5,6"
"1,2,3" "1,3"
listagg is the rescue (instead of count as commented by #Juan)
with lst as (
select c1, LISTAGG(c2, '; ') WITHIN GROUP (ORDER BY c2) c2_lst
from tst
group by c1
)
select lst1.c1 c1a, lst2.c1 c1b
from lst lst1
inner join lst lst2
on lst1.c2_lst = lst2.c2_lst and
lst1.c1 < lst2.c1
;
gives as requested
1 3
SQLFiddleDemo
Also be prepared, this works only small data (the concatenated key is limited with 4000 bytes).
UPDATE
listagg concatenates all values in the group. To get only distinct values (i.e. set of values) an additional query with DISTINCT must be performed.
WITH lst AS
( SELECT DISTINCT c1,c2 FROM tst
),
lst_dist AS
(SELECT c1,
LISTAGG(c2, '; ') WITHIN GROUP (
ORDER BY c2) c2_lst
FROM lst
GROUP BY c1
)
SELECT lst1.c1 c1a,
lst2.c1 c1b
FROM lst_dist lst1
INNER JOIN lst_dist lst2
ON lst1.c2_lst = lst2.c2_lst
AND lst1.c1 < lst2.c1
You can do this without list agg, using a self join:
select t1.c1, t2.c1
from (select t.*, count(*) over (partition by c1) as cnt
from table t
) t1 join
(select t.*, count(*) over (partition by c1) as cnt
from table t
) t2
on t1.c2 = t2.c2 and t1.c1 < t1.c2 and t1.cnt = t2.cnt
group by t1.c1, t2.c1
having count(*) = max(t1.cnt);
Note: this assumes that there are no duplicate rows in the table. A slight variation can work in that case as well.
This joins the rows on the second column and then aggregates by the first. Along the way, it makes sure that the number of matching columns is the same in the two table and that all columns match.

Select only rows that have unique fields

What is an SQL command that checks for rows that have rows with no duplicate fields in them.
ex:
A A A B B B should not be in the resulting table.
Only rows such as A B C D E F
i.e. given data like:
A A A B B B
A B C D E F
A A B G H Q
Should return A B C D E F
There is no simple command to do this.
is seems an unusual requirement and possibly an indication that the table is not in first normal form if all columns are interchangeable.
The following works in Microsoft SQL Server
;With YourData AS
(
select 'A' as C1, 'A' as C2, 'A' as C3, 'B' as C4, 'B' as C5, 'B' as C6 UNION ALL
select 'A' as C1, 'B' as C2, 'C' as C3, 'D' as C4, 'E' as C5, 'F' as C6
)
SELECT *
FROM YourData
WHERE 1 =
( SELECT TOP 1 COUNT(*) AS Cnt
FROM (
SELECT C1 AS C
UNION ALL
SELECT C2
UNION ALL
SELECT C3
UNION ALL
SELECT C4
UNION ALL
SELECT C5
UNION ALL
SELECT C6
) D
GROUP BY C
ORDER BY Cnt DESC
)
Select distinc * returns unique ROWS not unique values from fields.
You should compare each column's value with others. (Assuming column types are the same). For example, for a 4 column table you should do smoething like:
SELECT Col1, Col2, Col3, Col4 FROM MyTable WHERE
Col1 NOT IN (Col2,Col3,Col4) AND
Col2 NOT IN (Col3,Col4) AND
Col3 <> Col4
SELECT DISTINCT * FROM tablename
SELECT DISTINCT col FROM tabl
SELECT * FROM
mytable
WHERE mytable.col1 != mytable.col2 != mytable.col3 ...