Big Query SQL sum values based on conditions - sql

I have a query where I am trying to combine tables and sum results based on a combination of two columns. I want to combine the group from table 2 and 3 to be matched with the name and count of table 1. Then, the count should be summed so each name is listed once. However, IF a person worked are group UU AND group Z, THEN he/she should needs to have their group Z count listed separately from their group UU count. This person would be listed twice if they worked as group Z and UU. I need to split out results based on a specific condition (the combination of Portal and Group in the example below)
Table 1
+--------+------+-------+
| Portal | Name | Count |
+--------+------+-------+
| A | Bob | 3 |
| A | Joe | 6 |
| B | Joe | 6 |
| B | Bob | 2 |
| C | Bob | 5 |
+--------+------+-------+
Table 2
+-------+------+
| Group | Name |
+-------+------+
| Z | Bob |
| Y | Joe |
+-------+------+
Table 3
+-------+------+
| Group | Name |
+-------+------+
| UU | Bob |
| UU | Jill |
+-------+------+
Output
+-------+------+-------+
| Group | Name | Count |
+-------+------+-------+
| Z | Bob | 8 |
| UU | Bob | 2 |
| Y | Joe | 12 |
+-------+------+-------+
Notice that Bob's Portal B gets counted separately because he is in Group UU. Group UU only works in portal B. Bob's Portal A and C gets summed together because he is group Z in those Portals. Meanwhile Joe gets all of his count summed in a single row because he is not UU at all. Thanks in advance for any insight.

Below is for BigQuery Standard SQL
Try below
#standardSQL
WITH table1 AS (
SELECT 'A' portal, 'Bob' name, 3 cnt UNION ALL
SELECT 'A', 'Joe', 6 UNION ALL
SELECT 'B', 'Joe', 6 UNION ALL
SELECT 'B', 'Bob', 2 UNION ALL
SELECT 'C', 'Bob', 5
), table2 AS (
SELECT 'Z' grp, 'Bob' name UNION ALL
SELECT 'Y', 'Joe'
), table3 AS (
SELECT 'UU' grp, 'Bob' name UNION ALL
SELECT 'UU', 'Jill'
), all_groups AS (
SELECT * FROM table2 UNION ALL
SELECT * FROM table3
)
SELECT grp, name,
SUM(
CASE
WHEN grp = 'UU' THEN
CASE WHEN portal = 'B' THEN cnt ELSE 0 END
WHEN grp != 'UU' AND flag THEN
CASE WHEN portal = 'B' THEN 0 ELSE cnt END
ELSE cnt
END
) cnt
FROM (
SELECT a.grp, a.name, b.portal, b.cnt,
0 != COUNTIF(grp = 'UU') OVER(PARTITION BY a.name) flag
FROM all_groups a
JOIN table1 b
ON a.name = b.name
)
GROUP BY grp, name
with output
Row grp name cnt
1 Z Bob 8
2 UU Bob 2
3 Y Joe 12

I think you are trying to use a different mapping table depending on the value of the portal. If so, you can do:
select
case when portal = 'B' then t3.group else t2.group end as grp,
t1.name,
sum(count) cnt
from table1 t1
inner join table2 t2 on t2.name = t1.name
inner join table3 t2 on t2.name = t1.name
group by 1, 2
If you may have missing records in any of the group table, then use left join instead.

Related

Join number of pairs in a single table using SQL

I have two tables of events in bigquery that look like as follows. The main idea is two count the number of events in each table (are always pairs of event_id and user_id) and join them in a single table that for each pair in any table it tells the number of events.
table 1:
| event_id | user id |
| -------- | ------- |
| 1 | 1 |
| 2 | 1 |
| 2 | 3 |
| 2 | 5 |
| 1 | 1 |
| 4 | 7 |
table 2:
| event_id | user id |
| -------- | ------- |
| 1 | 1 |
| 3 | 1 |
| 2 | 3 |
I would like to get a table which has the number of events of each table:
| event_id | user id | num_events_table1 | num_events_table2 |
| -------- | ------- | ----------------- | ----------------- |
| 1 | 1 | 2 | 1 |
| 2 | 1 | 1 | 0 |
| 2 | 3 | 1 | 1 |
| 2 | 5 | 1 | 0 |
| 4 | 7 | 1 | 0 |
| 3 | 1 | 0 | 1 |
Any idea of how to do this with sql? I have tried this:
SELECT i1, e1, num_viewed, num_displayed FROM
(SELECT id as i1, event as e1, count(*) as num_viewed
FROM table_1
group by id, event) a
full outer JOIN (SELECT id as i2, event as e2, count(*) as num_displayed
FROM table_2
group by id, event) b
on a.i1 = b.i2 and a.e1 = b.e2
This is not getting exactly what I want. I amb getting i1 which are null and e1 that are null.
Consider below
#standardSQL
with `project.dataset.table1` as (
select 1 event_id, 1 user_id union all
select 2, 1 union all
select 2, 3 union all
select 2, 5 union all
select 1, 1 union all
select 4, 7
), `project.dataset.table2` as (
select 1 event_id, 1 user_id union all
select 3, 1 union all
select 2, 3
)
select event_id, user_id,
countif(source = 1) as num_events_table1,
countif(source = 2) as num_events_table2
from (
select 1 source, * from `project.dataset.table1`
union all
select 2, * from `project.dataset.table2`
)
group by event_id, user_id
if applied to sample data in your question - output is
If I understand correctly, the simplest method is to modify your query via a USING clause along with COALESCE():
SELECT id, event, COALESCE(num_viewed, 0), COALESCE(num_displayed, 0)
FROM (SELECT id, event, count(*) as num_viewed
FROM table_1
GROUP BY id, event
) t1 FULL JOIN
(SELECT id , event, COUNT(*) as num_displayed
FROM table_2
GROUP BY id, event
) t2
USING (id, event);
Note: This requires that the two columns used for the JOIN have the same name. If this is not the case, then you might still need column aliases in the subqueries.
One way is aggregate the union
select event_id, user id, sum(cnt1) cnt1, sum(cnt2) cnt2
from (
select event_id, user id, 1 cnt1, 0 cnt2
from table_1
union all
select event_id, user id, 0 cnt1, 1 cnt2
from table_2 ) t
group by event_id, user id

copy one table to another table with diffrent columns

I have a TableA columns are (id,name,A,B,C,p_id)
i want convert TableA to TableB, TableB columns are (id,name,alphabets,alphabets_value,p_id)
Record in TableA
id | name | A | B | C | p_id
1 | xyz | a | b | | 1
2 | opq | a`| b`| c`| 1
Expected In TableB
u_id | id | name | alphabets | alphabets_value | p_id
1 | 1 | xyz | A | a | 1
2 | 1 | xyz | B | b | 1
3 | 2 | opq | A | a` | 1
4 | 2 | opq | B | b` | 1
5 | 2 | opq | C | c` | 1
i want TableB output currently using Microsoft SQL
This is an unpivot, probably most easily explained by a UNION ALL:
SELECT id, name, 'A' as alphabets, a as alphabets_value, p_id
UNION ALL
SELECT id, name, 'B' as alphabets, b as alphabets_value, p_id
UNION ALL
SELECT id, name, 'C' as alphabets, c as alphabets_value, p_id
You can then WHERE to remove the nulls from this, and ROW_NUMBER to give yourself a fake U_id:
SELECT ROW_NUMBER() OVER(ORDER BY id, alphabets) as u_id, x.*
FROM
(
SELECT id, name, 'A' as alphabets, a as alphabets_value, p_id
UNION ALL
SELECT id, name, 'B' as alphabets, b as alphabets_value, p_id
UNION ALL
SELECT id, name, 'C' as alphabets, c as alphabets_value, p_id
)
WHERE
x.alphabets_value IS NOT NULL
Once you get to having a result set you want, INSERT INTO, UPDATE FROM or MERGE to get it into table B is quite trivial

how to select rows with same column_a but different column_b?

I want to select rows in sql server, there's my questions below:
Table1
--------------------------
| Name | Type |
--------------------------
| A | 1 |
| A | 2 |
| B | 1 |
| B | 3 |
| A | 3 |
| C | 1 |
| C | 3 |
| D | 1 |
| D | 2 |
| D | 3 |
| . | . |
| . | . |
Select rows like below:
Table2
--------------------------
| Name | Type |
--------------------------
| A | 1 |
| A | 2 |
| A | 3 |
| D | 1 |
| D | 2 |
| D | 3 |
| . | . |
| . | . |
The select rules is...
Show Name and Type which Type must have 1,2 and 3.
Example: A had 1,2,3 types,so i would select it.
Example: B only has 1,2 types,so i wouldn't select it.
You can use window functions for this:
select name, type
from (
select
t.*,
sum(case when type in (1, 2, 3) then 1 else 0 end)
over(partition by name) cnt
from mytable t
) t
where cnt = 3
This assumes that each (name, type) tuple occurs only once in the original table, which is consistant with your sample data.
Demo on DB Fiddle:
name | type
:--- | ---:
A | 1
A | 2
A | 3
D | 1
D | 2
D | 3
You could use INNER JOINs on the three Type columns to achieve this:
SELECT Table1.[Name],
Table1.[Type]
FROM Table1
INNER JOIN (
SELECT [Name]
FROM Table1
WHERE ([Type] = 1)
) A ON A.[Name] = Table1.[Name]
INNER JOIN (
SELECT [Name]
FROM Table1
WHERE ([Type] = 2)
) B ON B.[Name] = A.[Name]
INNER JOIN (
SELECT [Name]
FROM Table1
WHERE ([Type] = 3)
) C ON C.[Name] = A.[Name]
This outputs:
Name Type
A 1
A 2
A 3
D 1
D 2
D 3
The matching sqlfiddle.
This works by returning rows that contain [Type] = 1, and then ONLY matching rows where [Type] = 2 and [Type] = 3. Then this is joined back to your main table and the results are returned.
Get the names with group by name and set the condition in the having clause:
select * from Table1
where name in (
select name
from Table1
group by name
having count(distinct type) = 3
)
If there are for the column Type other values than 1, 2, 3 then:
select * from Table1
where type in (1, 2, 3) and name in (
select name
from Table1
where type in (1, 2, 3)
group by name
having count(distinct type) = 3
)
See the demo.
Results:
> Name | Type
> :--- | ---:
> A | 1
> A | 2
> A | 3
> D | 1
> D | 2
> D | 3
you can use string_agg if it is sql server 2017 and above or Azure SQL as below:
Select * from #yourTable yt join (
select [name], string_agg([Type], ',') as st_types
from #YourTable
group by [name] ) a
on yt.name = a.[name] and a.st_types like '%1,2,3%'
I give you this, this will work if you have:
A 1
A 2
A 3
A 2
It will then only give you B.
SELECT *
FROM Table1
WHERE Name in (
SELECT Name from
(
SELECT Name, Type, count(Name) c from Table1 where Type = 1
GROUP BY Name, Type
HAVING count(Name) = 1
UNION
SELECT Name, Type, count(Name) c from Table1 where Type = 2
GROUP by Name, Type
HAVING count(Name) = 1
UNION
SELECT Name, Type, count(Name) c from Table1 where Type = 3
GROUP by Name, Type
HAVING count(Name) = 1) t
GROUP by name
HAVING count(c) = 3)
Here is the DEMO

Select specific row but based on another column role

I tried and read many posts but I still can't figure out how to handle this request:
I have a table like below
+-------+---------------+------------+
| ID | Comp. | Role |
+-------+---------------+------------+
| 1 | abc | All |
+-------+---------------+------------+
| 1 | abc | Sales |
+-------+---------------+------------+
| 2 | def | All |
+-------+---------------+------------+
| 3 | zeh | All |
+-------+---------------+------------+
| 3 | zeh | TI |
+-------+---------------+------------+
I would like result role based, if one specific id have two role one is All and other is Sales then I need to consider Sales row
The output should be something like this
+-------+---------------+------------+
| ID | Comp. | Role |
+-------+---------------+------------+
| 1 | abc | Sales |
+-------+---------------+------------+
| 2 | def | All |
+-------+---------------+------------+
| 3 | zeh | TI |
+-------+---------------+------------+
UNION ALL will take the place
select * from table
where role <> 'All'
union all
select * from table t
where not exists (
select 1 from table
where id = t.id and [comp.] = t.[comp.] and role <> 'All'
)
order by 1
Result :
ID Comp. Role
1 abc Sales
2 def All
3 zeh IT
Hmmm . . . I think this does what you want:
select t.*
from t
where t.role = 'Sales'
union all
select t.*
from t
where t.role <> 'Sales' and
not exists (select 1 from t t2 where t2.id = t.id);
This will work if you have only 2 values for Role ('Sales' And 'All')
create table #tmp(ID INT,Comp VARCHAR(10),Role VARCHAR(10))
insert into #tmp
SELECT 1,'abc','All'
union ALL
SELECT 1,'abc','Sales'
union ALL
SELECT 2,'def','All'
select ID,Comp,MAX(Role) As Role from #tmp
Group by Id,comp
drop table #tmp
If you have other functions you should add then to the CASE WHEN
SELECT
ID
, Comp
, CASE WHEN role_id = 0 THEN 'All'
WHEN role_id = 1 THEN 'Sales'
WHEN role_id = 2 THEN 'TI'
ELSE NULL END AS Role
FROM
(
SELECT
ID
, Comp
, MAX(CASE WHEN Role = 'All' THEN 0
WHEN Role = 'Sales' THEN 1
WHEN Role = 'TI' THEN 2 END
ELSE -1 END) AS role_id
FROM
t
GROUP BY
ID
, Comp
)tmp
According to my understanding
SELECT ID,Comp,Role
FROM table1
WHERE ID IN(SELECT ID FROM table1 GROUP BY ID) AND Role != 'All'
UNION
SELECT ID,Comp,Role
FROM table1
WHERE ID IN(SELECT ID FROM table1 GROUP BY ID HAVING COUNT(ID)=1)
this is working fine check this demo

SQL Sum and Group by changing value of group

How do I group values ​​and change one of them when there is more than one
Table
ID | VALUE | NAME
1 | 2 | John
1 | 5 | Carl
2 | 4 | Elis
2 | 1 | Ted
3 | 2 | James
RESULT
ID | VALUE | NAME
1 | 7 | *
2 | 5 | *
3 | 2 | James
Here is one way that should work in any database:
select id, sum(value) as value,
(case when min(name) = max(name) then min(name) else '*' end) as name
from t
group by id;
This is the others query you must try
Select ID, sum(Value) Value, NAME = ( Select b.Name + ' ' AS [text()]
From dbo.test b
Where a.ID = b.ID
ORDER BY a.ID
For XML PATH (''))
from test a
group by ID
order by ID