SUM query's result which contain UNION ALL and CTE - sql

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'

Related

SQL query for finding records where count < 2

I have a table called Customer:
|device_id|user_id|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
| 6 | 2 |
| 7 | 3 |
Now I want to return only the entries which have only 1 device per user. In this case only
|device_id|user_id|
| 7 | 3 |
Should be returned because user_id 3 is the only one with only 1 device (user_id 1 has 4, user_id 2 has 2)
How would I do that with a query?
One method is not exists:
select t.*
from t
where not exists (select 1
from t t2
where t2.user_id = t.user_id and t2.device_id <> t.device_id
);
You can also use aggregation:
select device_id, max(user_id) as user_id
from t
group by device_id
having count(*) = 1;
We can use group by to group the data on the basis of user_id followed by aggregate funciton to get the count of device:
SELECT device_id,user_id FROM customer where user_id IN
(
SELECT user_id from
(
SELECT user_id,count(*) FROM customer GROUP BY user_id HAVING count(*)<2
)
);

SQL - How to find which page is the first for users?

I have a table like this:
+----------+-------------------------------------+----------------------------------+
| user_id | time | url |
+----------+-------------------------------------+----------------------------------+
| 1 | 02.04.2017 8:56 | www.landingpage.com/ |
| 1 | 02.04.2017 8:57 | www.landingpage.com/about-us |
| 1 | 02.04.2017 8:58 | www.landingpage.com/faq |
| 2 | 02.04.2017 6:34 | www.landingpage.com/about-us |
| 2 | 02.04.2017 6:35 | www.landingpage.com/how-to-order |
| 3 | 03.04.2017 9:11 | www.landingpage.com/ |
| 3 | 03.04.2017 9:12 | www.landingpage.com/contact |
| 3 | 03.04.2017 9:13 | www.landingpage.com/about-us |
| 3 | 03.04.2017 9:14 | www.landingpage.com/our-legacy |
| 3 | 03.04.2017 9:15 | www.landingpage.com/ |
+----------+-------------------------------------+----------------------------------+
I want to figure out which page is the first for most users (first page a user see when he comes to the site) and count the number of times it is viewed as the first page.
Is there a way to write a query to do this? I guess I need to use
MIN(time)
in conjunction with grouping but I don't know how.
So regarding the sample I provided it should be like:
url url_count
---------------------------------------------------
www.landingpage.com/ 2
www.landingpage.com/about-us 1
Thanks!
You're correct, you'll need to use the min() aggregate function within a subselect.
select
my_table.url
from
my_table
where
my_table.time = (
select
min(t.time)
from
my_table t
where
t.user_id = my_table.user_id
)
replace my_table with whatever your table is actually named.
To include how many pages the user has seen, you'll need something like this:
select
my_table.url
, (
select
count(t.url)
from
my_table t
where
t.user_id = my_table.user_id
) as url_count
from
my_table
where
my_table.time = (
select
min(t.time)
from
my_table t
where
t.user_id = my_table.user_id
)
SELECT *
FROM my_table
WHERE time IN
(
SELECT min(time)
FROM my_table
GROUP BY url
);
You can query as below:
Select top (1) with ties *
from yourtable
order by row_number() over(partition by user_id order by [time])
You can use outer query to get the same as below:
Select * from (
Select *, RowN = row_number() over(partition by user_id order by [time]) from yourtable) a
Where a.RowN = 1

Postgresql - Return (N) rows for each ID

I have a table like this
contact_id | phone_number
1 | 55551002
1 | 55551003
1 | 55551000
2 | 55552001
2 | 55552008
2 | 55552003
2 | 55552007
3 | 55553001
3 | 55553002
3 | 55553009
3 | 55553004
4 | 55554000
I want to return only 3 numbers of each contact_id, order by phone_number, like this:
contact_id | phone_number
1 | 55551000
1 | 55551002
1 | 55551003
2 | 55552001
2 | 55552003
2 | 55552007
3 | 55553001
3 | 55553002
3 | 55553004
4 | 55554000
please need be an optimized query.
My Query
SELECT a.cod_cliente, count(a.telefone) as qtd
FROM crm.contatos a
LEFT JOIN (
SELECT *
FROM crm.contatos b
LIMIT 3
) AS sub_contatos ON sub_contatos.cod_contato = a.cod_cliente
group by a.cod_cliente;
This type of query can easily be solved using window functions:
select contact_id, phone_number
from (
select contact_id, phone_number,
row_Number() over (partition by contact_id order by phone_number) as rn
from crm.contatos
) t
where rn <= 3
order by contact_id, phone_number;

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

SELECT only latest record of an ID from given rows

I have this table shown below...How do I select only the latest data of the id based on changeno?
+----+--------------+------------+--------+
| id | data | changeno | |
+----+--------------+------------+--------+
| 1 | Yes | 1 | |
| 2 | Yes | 2 | |
| 2 | Maybe | 3 | |
| 3 | Yes | 4 | |
| 3 | Yes | 5 | |
| 3 | No | 6 | |
| 4 | No | 7 | |
| 5 | Maybe | 8 | |
| 5 | Yes | 9 | |
+----+---------+------------+-------------+
I would want this result...
+----+--------------+------------+--------+
| id | data | changeno | |
+----+--------------+------------+--------+
| 1 | Yes | 1 | |
| 2 | Maybe | 3 | |
| 3 | No | 6 | |
| 4 | No | 7 | |
| 5 | Yes | 9 | |
+----+---------+------------+-------------+
I currently have this SQL statement...
SELECT id, data, MAX(changeno) as changeno FROM Table1 GROUP BY id;
and clearly it doesn't return what I want. This should return an error because of the aggrerate function. If I added fields under the GROUP BY clause it works but it doesn't return what I want. The SQL statement is by far the closest I could think of. I'd appreciate it if anybody could help me on this. Thank you in advance :)
This is typically referred to as the "greatest-n-per-group" problem. One way to solve this in SQL Server 2005 and higher is to use a CTE with a calculated ROW_NUMBER() based on the grouping of the id column, and sorting those by largest changeno first:
;WITH cte AS
(
SELECT id, data, changeno,
rn = ROW_NUMBER() OVER (PARTITION BY id ORDER BY changeno DESC)
FROM dbo.Table1
)
SELECT id, data, changeno
FROM cte
WHERE rn = 1
ORDER BY id;
You want to use row_number() for this:
select id, data, changeno
from (SELECT t.*,
row_number() over (partition by id order by changeno desc) as seqnum
FROM Table1 t
) t
where seqnum = 1;
Not a well formed or performance optimized query but for small tasks it works fine.
SELECT * FROM TEST
WHERE changeno IN (SELECT MAX(changeno)
FROM TEST
GROUP BY id)
for other alternatives :
DECLARE #Table1 TABLE
(
id INT, data VARCHAR(5), changeno INT
);
INSERT INTO #Table1
SELECT 1,'Yes',1
UNION ALL
SELECT 2,'Yes',2
UNION ALL
SELECT 2,'Maybe',3
UNION ALL
SELECT 3,'Yes',4
UNION ALL
SELECT 3,'Yes',5
UNION ALL
SELECT 3,'No',6
UNION ALL
SELECT 4,'No',7
UNION ALL
SELECT 5,'Maybe',8
UNION ALL
SELECT 5,'Yes',9
SELECT Y.id, Y.data, Y.changeno
FROM #Table1 Y
INNER JOIN (
SELECT id, changeno = MAX(changeno)
FROM #Table1
GROUP BY id
) X ON X.id = Y.id
WHERE X.changeno = Y.changeno
ORDER BY Y.id