Select specific row but based on another column role - sql

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

Related

Big Query SQL sum values based on conditions

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.

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

SQL to group by 2 IDs

So I have a table that's laid out like this:
table1:
ID | metric1 | metric2
A | 1 | 1
A | 1 | 1
B | 2 | 3
C | 3 | 2
And another table that may have an alternate ID an item may have (note that the new ID will also be in the table above). Example:
conversions:
old_ID | new_ID
A | C
So I'm trying to create a query that aggregates both on the new ID and the old-ID, but also preserves the old-ID if available. So basically the results I want look like this:
ID | potential_old_ID | metric1 | metric2
C | A | 5 | 4
B | NULL | 2 | 3
So far with my current strategy I've been able to get close with a query like this:
select
(CASE WHEN new_ID is null then ID else new_ID END) as ID,
(CASE WHEN new_ID is null then null else ID END) as potential_old_ID,
SUM(metric1),
SUM(metric2)
from table1
left join conversions on ID = old_ID
group by ID, new_ID
Which get's me close, but it still separates C and A in separate rows, which doesn't work for my use case:
ID | potential_old_ID | metric1 | metric2
C | A | 2 | 2
B | NULL | 2 | 3
C | NULL | 3 | 2
If I remove the new_ID from the group by I get an error on the query. Anyway I can get the results I'm looking for that I'm missing?
You need to make sure that the rows that have C already also have the same potential old ID as the ones that are A. Something like
SELECT ID, potential_old_ID, SUM(metric1), SUM(metric2)
FROM
( select
(CASE WHEN c1.new_ID is null then ID else c1.new_ID END) as ID,
COALESCE(c1.old_ID, c2.old_ID) as potential_old_ID,
metric1,
metric2
from table1
left join conversions c1 on ID = c1.old_ID
left join conversions c2 on ID = c2.new_ID
) AS data
GROUP BY ID, potential_old_ID
Hmmmm . . . I think this does what you want:
select coalesce(new_id, id) as id,
SUM(metric1),
SUM(metric2)
from table1 left join
conversions
on ID = old_ID
group by coalesce(new_id, id);

condition in returning number of column

I have table friends below.
-----------------------------
| id | user_id | friends_id |
-----------------------------
| 1 | 1 | 2 |
-----------------------------
| 2 | 1 | 3 |
-----------------------------
| 3 | 1 | 4 |
-----------------------------
| 4 | 2 | 1 |
-----------------------------
| 5 | 3 | 5 |
-----------------------------
| 6 | 4 | 5 |
-----------------------------
and SQL query:
select user_id, friends_id from friends where user_id = 1
Above query gives me below result.
------------------------
| user_id | friends_id |
------------------------
| 1 | 2 |
------------------------
| 1 | 3 |
------------------------
| 1 | 4 |
------------------------
I want to get resul like this:
------------------------
| user_id | friends_id |
------------------------
| 1 | many |
------------------------
If any user have friends more than 1, i must write word 'many'.
If any user have 1 friend, i must write his ID.
How i can do that?
I googled and can't explain to google my problem.
Do a GROUP BY to count number of friends. Use a CASE to return 'Many' if more than 1 friend, or the friend_id if only one (need to CAST to a character type, compatible with 'Many'.)
select user_id,
case when count(friends_id) > 1 then 'Many'
else cast(min(friends_id) as varchar(11)) end as friends_id
from friends
group by user_id
I update the query and include
SQL FIDDLE DEMO
WITH fCount as (
SELECT user_id, min(friends_id) friends_id, count(friends_id) nFriend
FROM friends
GROUP BY user_id
)
select user_id, CASE
WHEN nFriend > 1 THEN 'many'
ELSE cast(friends_id as nvarchar(10))
END friends_id
from fCount
try this
select user_id, CASE
WHEN count(friends_id) > 1 THEN 'many'
ELSE friends_id
END friends_id
from friends
where user_id = 1
Hi you can use below query:
CREATE TABLE #Temp (
ID INT Identity(1, 1)
,UID INT
,f_id NVARCHAR(10)
)
INSERT INTO #temp
SELECT 1
,2
UNION ALL
SELECT 1
,3
UNION ALL
SELECT 1
,4
UNION ALL
SELECT 2
,1
SELECT uid
,COUNT(f_id) AS COUNT_of_Friends
INTO #Temp2
FROM #temp
GROUP BY Uid
SELECT t1.Uid
,CASE
WHEN t2.COUNT_of_Friends > 1
THEN 'Many'
ELSE f_id
END AS F_id
FROM #temp T1
INNER JOIN #Temp2 T2 ON T1.Uid = t2.Uid

SUM query's result which contain UNION ALL and CTE

Even though I found something similar to what I was needed, I wasn't able to work it out.
So, I'll ask using my example.
(It is in continuation to my prev question: select if there are more than n results but with conditions)
My Table:
| TimeId | Work_Role | User_Name |
----------------------------------
| 1 | users | Oran |
| 2 | admin | Ray |
| 3 | users | Oran |
| 4 | servs | Amit |
| 5 | admin | Oran |
| 6 | users | Ray |
| 7 | users | Oran |
| 8 | servs | Amit |
| 9 | admin | Oran |
| 10 | users | Oran |
I've used CTE to display a list for user_name = "Oran" and Work_Role = "users" ONLY if there are more than 2 in the table.
In addition, I used Union all to gather another part that it's condition is: user_name = "Oran" and Work_Role = "admin".
The result is:
| TimeId | Work_Role | User_Name |
----------------------------------
| 1 | users | Oran |
| 3 | users | Oran |
| 7 | users | Oran |
| 10 | users | Oran |
| 5 | admin | Oran |
| 9 | admin | Oran |
My second part (which there I have the problem) is to count how many lines the result above has.
In this case I expect: 6
My code for the first result (which is working fine):
WITH CTE AS(
SELECT *, cc = COUNT(*) OVER(PARTITION BY User_Name) FROM Table
)
SELECT *
FROM CTE
WHERE
Work_Role = 'users'
AND User_Name = 'Oran'
AND cc > 2
UNION ALL
SELECT *
FROM TABLE
WHERE
Work_Role = 'admin'
AND User_Name = 'Oran'
How can I summarize the lines' number of the final result?
Can it even be done while using CTE?
NOTE:
This is just an example. In my real code I must use the "UNION ALL" :-(
10X!
First, you can simplify the query to:
WITH CTE AS (
SELECT t.*, cc = COUNT(*) OVER(PARTITION BY User_Name) FROM Table t
)
SELECT *
FROM CTE
WHERE (Work_Role = 'users' AND User_Name = 'Oran' AND cc > 2) OR
(Work_Role = 'admin' AND User_Name = 'Oran');
The nice thing is that this will also work, because the two subqueries have the same columns.
If you want to count the number of rows, just use select count(*):
WITH CTE AS (
SELECT t.*, cc = COUNT(*) OVER(PARTITION BY User_Name) FROM Table t
)
SELECT COUNT(*)
FROM CTE
WHERE (Work_Role = 'users' AND User_Name = 'Oran' AND cc > 2) OR
(Work_Role = 'admin' AND User_Name = 'Oran');
Ok, eventually I've found an answer, lol
It is based on the first answer of this question: Sum a union query
I had to alias my derived table.
Set the additional select (with the SUM) between the "THIS" and it's "SELECT".
Change to COUNT for each query and alias it.
Like so:
WITH CTE AS(
SELECT *, cc = COUNT(*) OVER(PARTITION BY User_Name) FROM Table
)
SELECT SUM(my_dervied_table.total_lines) FROM (
SELECT COUNT(*) as 'total_lines'
FROM CTE
WHERE
Work_Role = 'users'
AND User_Name = 'Oran'
AND cc > 2
UNION ALL
SELECT COUNT(*) as 'total_lines'
FROM TABLE
WHERE
Work_Role = 'admin'
AND User_Name = 'Oran'
) my_dervied_table
So instead of counting the lines in this output:
| TimeId | Work_Role | User_Name |
----------------------------------
| 1 | users | Oran |
| 3 | users | Oran |
| 7 | users | Oran |
| 10 | users | Oran |
| 5 | admin | Oran |
| 9 | admin | Oran |
I'm actually summing the 'total_lines' column's content of the "my_derived_table": 4+2 = 6
| total_lines |
---------------
1) | 4 |
2) | 2 |
shalom yael-try this code-
WITH CTE AS
(
SELECT *
FROM tbl
WHERE
Work_Role = 'users'
AND User_Name = 'Oran'
AND ( select COUNT(user_name)
FROM tbl
WHERE user_name='oran') > 2
UNION ALL
SELECT *
FROM tbl
WHERE
Work_Role = 'admin'
AND User_Name = 'Oran')
SELECT count([User_Name]) total_lines
from cte
GROUP BY [Work_Role],[User_Name]
Are you looking for this,
Declare #t table(TimeId int,Work_Role varchar(50),User_Names varchar(50))
insert into #t values
(1 , 'users','Oran' ),
( 2 ,'admin', 'Ray'),
( 3 ,'users', 'Oran'),
( 4,'servs', 'Amit'),
( 5,'admin', 'Oran'),
( 6,'users', 'Ray'),
( 7,'users', 'Oran'),
( 8,'servs', 'Amit'),
( 9,'admin', 'Oran'),
( 10,'users', 'Oran')
;With CTE as
(
select *,
ROW_NUMBER()over(partition by User_Names,Work_Role order by timeid)rn
from #t
)
select * from CTE where Work_Role = 'users'
AND User_Names = 'Oran'
AND exists
(
select User_Names from CTE where rn>2
and Work_Role = 'users' AND User_Names = 'Oran'
)
UNION ALL
select * from CTE where Work_Role = 'admin'
AND User_Names = 'Oran'