SQL add values of rows, if columns are switched - sql

After a join of the same table, I have a result like this:
c1 c2 count
A B 5
A C 4
B A 2
B C 2
C A 1
Now, the numbers should been added, if c1 and c2 are switched, like this:
c1 c2 count
A B 7
A C 5
B C 2
How can this be done with a query?

Using a left join to self join the table on inverse positions and returning those where c1 is less than c2, or it had no matching row. Using coalesce to add 0 when the left joined count is null.
select
t.c1
, t.c2
, t.count + coalesce(s.count,0) as count
from t
left join t as s
on t.c1 = s.c2
and t.c2 = s.c1
where t.c1 < t.c2 or s.c1 is null
rextester demo in sql server: http://rextester.com/VBQI62112
returns:
+----+----+-------+
| c1 | c2 | count |
+----+----+-------+
| A | B | 7 |
| A | C | 5 |
| B | C | 2 |
+----+----+-------+

Many databases support least() and greatest(). If they are available, you can do:
select least(c1, c2) as c1, greatest(c1, c2) as c2, sum(count) as cnt
from (<your query here>) t
group by least(c1, c2), greatest(c1, c2);
In databases that don't support these functions, you can use case.
Note: The semantics of least() and greatest() return NULL if either column is NULL, so you may need to be careful if either value could be NULL.

Perhaps join the output c1,c2 with the same c2,c1?
select t1.c1
,t1.c2
,sum(coalesce(t1.count,0), coalesce(t2.count,0))
from table t1
left join table t2
on t1.c1 = t2.c2
and t1.c2 = t2.c1
group by t1.c1, t1.c2
having t1.c1 < t1.c2

SELECT t.c1
, t.c2
, t.cnt + CASE WHEN s.cnt IS NULL THEN 0 ELSE s.cnt END as cnt
FROM t
LEFT JOIN
t as s
ON t.c1 = s.c2
AND t.c2 = s.c1
WHERE t.c1 < t.c2;

Related

find the max for each value in SQL

i have tables like this
table 1
|cl.1|
| -- |
| a |
| b |
| c |
table 2
|cl.1|cl.2|para|
|----|---| --- |
| a | 3 | t |
| a | 3 | f |
| b | 2 | t |
| a | 1 | b |
| c | 4 | t |
| b | 7 | d |
i want to get the max value for each element in table1 from table2
and the different parameter
so the expecited tabel should be like this
|cl.1|max|para|
|----|---| --- |
| a | 3 | t |
| a | 3 | f |
| c | 4 | t |
| b | 7 | d |
You can try to compute all the maximums:
with Maxes as (
select cl1,
max(cl2) as cl2
from Table2
group by cl1)
and then join them with the original Table2, e.g.
with Maxes as (
select cl1,
max(cl2) as cl2
from Table2
group by cl1)
select t.*
from Table2 t join
Maxes m on (t.cl1 = m.cl1 and t.cl2 = m.cl2)
Depends on what features your RDBMS supports.
With Oracle you could do a CROSS APPLY to order table2 by descending cl2 and keep the top values (with ties):
select T1.c1, TM.maximum, TM.para
from Table1 T1
cross apply (
select *
from Table2 T2
where T2.c1 = T1.c1
order by T2.maximum descending
fetch first 1 row with ties
) TM
You can do the same in SQL Server with syntax select top 1 with ties instead of fetch first 1 row with ties.
Another option could be to use Analytical Functions to rank the results per col1 and then keep only the first ones.
select T.c1, T.maximum, T.para
from (
select
T1.c1, T2.maximum, T2.para,
rank() over (partition by T1.c1 order by T2.maximum desc) r
from T1
join T2 on T1.c1 = T2.c2
) T
where T.r = 1
Less stylish and probably(?) less performant would be computing the maximum for each c1 and then doing an equality:
select T1.c1, T2.maximum, T2.para
from T1
join T2 on T1.c1 = T2.c1
where T2.maximum = (select max(maximum) from T2 where c1 = T1.c1)
If you are trying to get the max tl.1 and if for the same values it is equal, you could try:
SELECT *
FROM table2
WHERE cl_2 in ( SELECT MAX(cl_2)
FROM table2
group by cl_1
);
Result:
cl_1 cl_2 para
a 3 t
a 3 f
c 4 t
b 7 d
Tested on MySQL : https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=42a6bc20622a210b18101588540995ec
You could use a join , but it makes no difference:
SELECT t1.cl_1,t2.cl_2,t2.para
FROM table2 t2
INNER JOIN table1 t1 on t2.cl_1=t1.cl_1
WHERE t2.cl_2 in (SELECT MAX(cl_2) FROM table2 group by cl_1 );
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=4b2eed9bcee3532cc7c4e7b3862bc3ef
DENSE_RANK can be used to get whole rows that have a maximum of something within a partition.
Because when sorted descending, the top 1 will have rank 1.
select cl_1, cl_2, para
from
(
select cl_1, cl_2, para
, dense_rank() over (partition by cl_1 order by cl_2 desc) as rnk
from table1 t1
join table2 t2 using (cl_1)
) q
where rnk = 1
Use a CTE to get the max values, then select the rows with those values:
with maxes as
(
select t1.[cl.1]
, max(t2.[cl.2]) max_val
from table1 t1
inner join table2 t2
on t1.[cl.1] = t2.[cl.1]
group by t1.[cl.1]
)
select t1.[cl.1]
, t2.[cl.2]
, t2.para
from table1 t1
inner join table2 t2
on t1.[cl.1] = t2.[cl.1]
where t2.[cl.2] = (select m.max_val from maxes m where m.[cl.1] = t1.[cl.1])
This can also be achieved by joining the CTE:
with maxes as
(
select t1.[cl.1]
, max(t2.[cl.2]) max_val
from table1 t1
inner join table2 t2
on t1.[cl.1] = t2.[cl.1]
group by t1.[cl.1]
)
select t1.[cl.1]
, t2.[cl.2]
, t2.para
from table1 t1
inner join table2 t2
on t1.[cl.1] = t2.[cl.1]
inner join maxes m
on t2.[cl.2] = m.max_val

Self joining table with a condition

I have a table of the following type:
Table dummy1:
e_n t_s item
a t1 c
a t2 c
a t3 c
a t4 c
b p1 c
b p2 c
b p3 c
b p4 c
t1, t2, t3, t4, p1, p2, p3, p4 are timestamps in ascending order.
t1, t2, t3, t4 are timestamps in ascending order for event_name 'a'.
p1, p2, p3, p4 are timestamps in ascending order for event_name 'b'.
c is the item_number for which these events 'a' and 'b' are occurring.
I am trying to write a query for which the result should be as follows:
e_n1 e_n2 item t_s_1 t_s_2
a b c t1 p1
a b c t2 p2
a b c t3 p3
a b c t4 p4
I have tried the following code:
select l.e_n as e_n_1, m.e_n as e_n_2, l.item, l.t_s as t_s_a,
m.t_s as t_s_b from (
(select * from dummy where e_n = 'a') l
join
(select * from dummy where e_n = 'b') m
on l.item = m.item and l.t_s < m.t_s
The join l.item = m.item is needed as there are many other items c1, c2, c3 with the same structure
The result is:
e_n1 e_n2 item t_s_a t_s_b
a b c t1 p1
a b c t1 p2
a b c t1 p3
a b c t1 p4
a b c t2 p1
a b c t2 p2
a b c t2 p3
so on
How can I achieve my result in an efficient way?
select min (case when e_n = 'a' then 'a' end) as e_n1
,min (case when e_n = 'b' then 'b' end) as e_n2
,item
,min (case when e_n = 'a' then t_s end) as t_s_1
,min (case when e_n = 'b' then t_s end) as t_s_2
from (select d.*
,row_number () over (partition by item,e_n order by t_s) as rn
from dummy as d
) d
group by item
,rn
+------+------+------+-------+-------+
| e_n1 | e_n2 | item | t_s_1 | t_s_2 |
+------+------+------+-------+-------+
| a | b | c | t1 | p1 |
| a | b | c | t2 | p2 |
| a | b | c | t3 | p3 |
| a | b | c | t4 | p4 |
+------+------+------+-------+-------+
First, Sort by timestamp for every event then join on sorted table row number.
try below code.
select l.e_n as e_n_1, m.e_n as e_n_2, isnull(l.item,m.item) as item, l.t_s as t_s_a,
m.t_s as t_s_b from
(select *,(row_number() over (order by t_s)) as rn from dummy where e_n = 'a') l
full join
(select *,(row_number() over (order by t_s)) as rn from dummy where e_n = 'b') m
on l.item = m.item and l.rn=m.rn

Oracle left join returning no rows

I have a (now thoroughly derived) CTE:
feature_id | function_id | group_id | subgroup_id | type_id
1 1 null 1 null
1 1 null null 14
2 1 null 5 null
2 1 null null 21
3 1 null 7 null
3 1 null null 5
I am trying to collate the rows together using this:
select C1.feature_id, C1.function_Id, C2.Group_ID, C3.Subgroup_ID, C4.Type_id
from CTE C1
left join CTE C2
on C1.feature_id = C2.feature_id
and c1.function_id = c2.function_id
and c2.group_id is not null
left join CTE C3
on C1.feature_id = C3.feature_id
and c1.function_id = c3.function_id
and c3.subgroup_id is not null
left join CTE C4
on C1.feature_id = C4.feature_id
and c1.function_id = c4.function_id
and c4.type_id is not null
This gives me 0 rows...
To validate, I ran:
select *
from CTE C1
236 rows selected
Can anyone help? Surely the rows from C1 should be coming back...
EDIT: Fixed it with the Oracle syntax:
select C1.feature_id, C1.function_Id, C2.Group_ID, C3.Subgroup_ID, C4.Type_id
from CTE C1, CTE C2, CTE C3, CTE C4
where C1.feature_id = C2.feature_id(+)
and c1.function_id = c2.function_id(+)
and C2.group_id(+) is not null
...
(I hate the oracle syntax)
Ok, solved it... Fixed it with the Oracle syntax:
select C1.feature_id, C1.function_Id, C2.Group_ID, C3.Subgroup_ID, C4.Type_id
from CTE C1, CTE C2, CTE C3, CTE C4
where C1.feature_id = C2.feature_id(+)
and c1.function_id = c2.function_id(+)
and C2.group_id(+) is not null
...
(I hate the oracle syntax)

Query to update row which contains maximum column value in MS SQL 2008

I have a table similar to below:
C1 C2 C3
A 5 0
A 15 0
A 2 0
B 5 0
B 8 0
Result table updates C3 with 1 for mac value of C2 group by C1
C1 C2 C3
A 5 0
A 15 1
A 2 0
B 5 0
B 8 1
For SQL Server 2005+:
;WITH CTE AS
(
SELECT *,
MAX(C2) OVER(PARTITION BY C1) MaxC2
FROM YourTable
)
UPDATE CTE
SET C3 = CASE WHEN MaxC2 = C2 THEN 1 ELSE 0 END
Supossing table is named table_name
UPDATE t1 SET t1.C3 = 1
FROM table_name as t1 INNER JOIN (
SELECT C1, MAX(C2) AS C2 FROM table_name GROUP BY C1
) AS t2 ON t1.C1 = t2.C1 AND t1.C2 = t2.C2

Returning Unique rows from Left Outer Join

I am trying to build a query which will give me unique rows. Details:-
Table 1 (F1, F2 are the columns)
F1 F2
1 A1
2 A2
3 A3
4 A4
Table 2 (F3,F4 are the columns)
F3 F4
1 B1
1 B11
2 B2
2 B22
My Query (Incorrect)
select rrn(A), F1,F2,F3,F4 from rush2hem1.T1 A left outer join rush2hem1.T2 B on A.F1=B.F3
This gives me below output which is not what I am looking for:-
RRN F1 F2 F3 F4
1 1 A1 1 B1
1 1 A1 1 B11
2 2 A2 2 B2
2 2 A2 2 B22
3 3 A3 (null) (null)
4 4 A4 (null) (null)
Expected output that I am building query for is:-
RRN F1 F2 F3 F4
1 1 A1 1 B1
2 2 A2 2 B2
3 3 A3 (null) (null)
4 4 A4 (null) (null)
Please let me know if you have any suggestions.
This problem could be solved differently in different RDBMS. In any case, you have to specify which one record from Table2 do you want to get (by order by clause)
If your database have window function row_number(), you can use it like this:
select
F1, F2, F3, F4
from (
select
T1.F1, T1.F2, T2.F3, T2.F4,
-- order by specifying which row you would get from Table2
row_number() over(partition by T1.F1 order by T2.F4) as rn
from Table1 as T1
left outer join Table2 as T2 on T2.F3 = T1.F1
) as cte
where rn = 1;
In SQL Server, you can use outer apply:
select
T1.F1, T1.F2, T2.F3, T2.F4
from Table1 as T1
outer apply (
select top 1 T2.F3, T2.F4
from Table2 as T2
where T2.F3 = T1.F1
order by T2.F3 asc -- This is important line
) as T2;
In PostgreSQL, you can use distinct on syntax:
select distinct on (T1.F1)
T1.F1, T1.F2, T2.F3, T2.F4
from Table1 as T1
left outer join Table2 as T2 on T2.F3 = T1.F1
order by T1.F1, T2.F4; -- sort by F4 is important
SQL Server sqlfiddle demo
PostgreSQL sqlfiddle demo
Not tested, It's a SQL server version.
select rrn(A), F1,F2,F3,F4
from
(
select rrn(A), F1,F2,F3,F4,row_number() over(partition by RRN order by RRN) as rn
from rush2hem1.T1 A left outer join rush2hem1.T2 B
on A.F1=B.F3
) as dt
where dt.rn = 1
Please check the result with OUTER APPLY
SELECT
*
FROM
Table1 a
OUTER APPLY
(SELECT
TOP 1 *
FROM
Table2 b
WHERE a.F1=b.F3)c