How to select people who has multiple values - sql

I want to select people who has 2 values (activate & recurring) in the table for example,
table :: tbl_transactions
id
name
action
1
John
activate
2
John
recurring
3
Salah
activate
4
Bill
activate
5
Bill
recurring
6
Bill
recurring
Expected result,
id
name
action
1
John
activate
2
John
recurring
4
Bill
activate
5
Bill
recurring
6
Bill
recurring
Please help. I have been spent for an hour to fix this.
Really thanks.

You can aggregate the action values for each user name and check if the array is 2 long (since you only need 2 actions) and contains ['activate', 'recurring'] (since you only need these values)
SELECT t.id, t.name FROM tbl_transactions t
JOIN LATERAL (
SELECT
name,
ARRAY_AGG(action) AS actions
FROM tbl_transactions
GROUP BY name
) user_actions ON t.name = user_actions.name
AND ARRAY_LENGTH(actions, 1) = 2
AND ARRAY['activate', 'recurring']::VARCHAR[] #> actions
Demo

here is a query for selecting only names appearing twice:
select t.*
from t
join (select name, count(*)
from t
having count(*) = 2
) c on t.name = c.name ;

The query will be as below. Check Sql Fiddle. You can use count distinct action to count distinct action value check it is greater than 1.
select a.* from tbl_transactions a join
(select name, count(*) from tbl_transactions group by name having count(distinct action) > ) b
on a.name = b.name

I would use the bool_or() window function for this:
with complete_check as (
select id, name, action,
bool_or(action = 'recurring') over w as has_recurring,
bool_or(action = 'activate') over w as has_activate
from tbl_transactions
window w as (partition by name)
)
select id, name, action
from complete_check
where has_recurring and has_activate;
db<>fiddle here

Related

Query users that might interacted with on same category

I need to do a query to list all the users that may interacted with each other on the same category with a note. Basically, everyone that made a note on a category that the specific user made a note on. How can be done?
Let's say the USER ID is 3, that left a note (note_10):
ID U CATEGORY NOTE
1 3 5 'note_10'
2 1 3 'note_11'
3 2 5 'note_12'
4 5 2 'note_13'
5 6 5 'note_14'
6 7 5 'note_15'
Expected results:
U
2
6
7
ID number 2, 6, and 7 has posted on the same category.
I think you want a self-join with some filtering:
select t.*
from t join
t tt
on tt.category = t.category and
tt.note = 'note_10';
Here is a db<>fiddle.
If you want to filter out the original user, you can use:
from t join
t tt
on tt.category = t.category and
tt.note = 'note_10' and
tt.id <> t.id;
I think you want exists:
select t.u
from mytable t
where
t.u <> 3
and exists (select 1 from mytable t1 where t1.u = 3 and t1.category= t.category)
This gives you all users that posted on any category user 3 posted on.
This might generate duplicates in a given user has several categories in common with user 3 - if you want to avoid that, you can use select distinct instead.
Another option is window functions:
select u
from (
select u, count(*) filter(where u = 3) over(partition by category) cnt
from mytable
) t
where u <> 3 and cnt > 0

Oracle Create a view replacing ids with names in column (not 1nf)

We have for example this table:
pl_num camp_type products
1 T 1,2,3
2 B 1,3,4
Yeah, I know it's not in 1NF but we need to work with it
because of application loads data in such way.
And we have table DICT_PRODUCT, for example (in reality, there are more than 500 product):
id product_name
1 a
2 b
3 c
4 d
So, what we need is to create view where product_id was replaced by its name in dictionary
---V_TAB1 ---
pl_num camp_type products
1 T 1,b,c
2 B a,c,d
Try this. It will work if products column in TAB1 contain numbers and not any other characters.
WITH prod
AS (SELECT pl_num, camp_type, TO_NUMBER (TRIM (COLUMN_VALUE)) product
FROM Tab1 t, XMLTABLE (t.products))
SELECT prod.pl_num,
prod.camp_type,
LISTAGG (d.product_name, ',') WITHIN GROUP (ORDER BY id) products
FROM prod JOIN dict_product d ON prod.product = d.id
GROUP BY prod.pl_num, prod.camp_type;
DEMO
Try this one:
select distinct *
from (
select t.u_name, u_id, regexp_substr(t.prod,'[^,]+', 1, level) id
from (select prod,u_id, u_name from cmdm.t_prod) t
connect by regexp_substr(prod,'[^,]+',1,level) is not null) ut
inner join cmdm.t_dct dt
on ut.id=dt.id

Select rows in one table, adding column where MAX(Date) of rows in other, related table

I have a table containing a set of tasks to perform:
Task
ID Name
1 Washing Up
2 Hoovering
3 Dusting
The user can add one or more Notes to a Note table. Each note is associated with a task:
Note
ID ID_Task Completed(%) Date
11 1 25 05/07/2013 14:00
12 1 50 05/07/2013 14:30
13 1 75 05/07/2013 15:00
14 3 20 05/07/2013 16:00
15 3 60 05/07/2013 17:30
I want a query that will select the Task ID, Name and it's % complete, which should be zero if there aren't any notes for it. The query should return:
ID Name Completed (%)
1 Washing Up 75
2 Hoovering 0
3 Dusting 60
I've really been struggling with the query for this, which I've read is a "greatest n per group" type problem, of which there are many examples on SO, none of which I can apply to my case (or at least fully understand). My intuition was to start by finding the MAX(Date) for each task in the note table:
SELECT ID_Task,
MAX(Date) AS Date
FROM
Note
GROUP BY
ID_Task
Annoyingly, I can't just add "Complete %" to the above query unless it's contained in a GROUP clause. Argh! I'm not sure how to jump through this hoop in order to somehow get the task table rows with the column appended to it. Here is my pathetic attempt, which fails as it only returns tasks with notes and then duplicates task records at that (one for each note, so it's a complete fail).
SELECT Task.ID,
Task.Name,
Note.Complete
FROM
Task
JOIN
(SELECT ID_Task,
MAX(Date) AS Date
FROM
Note
GROUP BY
ID_Task) AS InnerNote
ON
Task.ID = InnerNote.ID_Task
JOIN
Note
ON
Task.ID = Note.ID_Task
Can anyone help me please?
If we assume that tasks only become more complete, you can do this with a left outer join and aggregation:
select t.ID, t.Name, coalesce(max(n.complete), 0)
from tasks t left outer join
notes n
on t.id = n.id_task
group by t.id, t.name
If tasks can become "less complete" then you want the one with the last date. For this, you can use row_number():
select t.ID, t.Name, coalesce(n.complete, 0)
from tasks t left outer join
(select n.*, row_number() over (partition by id_task order by date desc) as seqnum
from notes n
) n
on t.id = n.id_task and n.seqnum = 1;
In this case, you don't need a group by, because the seqnum = 1 performs the same role.
How about this just get the max of completed and group by taskid
SELECT t.ID_Task as ID,n.`name`,MAX(t.completed) AS completed
FROM `task` t RIGHT JOIN `note` n on ( t.ID_Task=n.ID )
GROUP BY t. ID_Task
OR
SELECT t.ID_Task as ID,n.`name`,
(CASE when MAX(t.completed) IS NULL THEN '0' ELSE MAX(t.completed))AS completed
FROM `task` t RIGHT JOIN `note` n on ( t.ID_Task=n.ID )
GROUP BY t. ID_Task
select a.ID,
a.Name,
isnull((select completed
from Note
where ID_Task = b.ID_Task
and Date = b.date),0)
from Task a
LEFT OUTER JOIN (select ID_Task,
max(date) date
from Note
group by ID_Task) b
ON a.ID = b.ID_Task;
See DEMO here

SQL Query help for single table query

I have a table that records status on course progress. A new record is added for each user/course comination when a course is started. That record is updated with a 'completed' status when the course is completed. I need to find the records for users who have never completed any courses.
Example Table:
User Course Status
A 1 S
A 2 C
B 1 S
C 2 S
D 2 C
C 3 S
I need a query that finds the following:
User Course Status
B 1 S
C 2 S
C 3 S
Any help is appreciated.
select user, course, status
from your_table
where user in
(
select user
from your_table
group by user
having sum(CASE WHEN status = 'C' THEN 1 ELSE 0 END) = 0
)
Select User, Course, Status from MyTable where User not in (Select Distinct User from MyTable where Status = 'C')
SELECT User,Course,Status FROM YourTable a
LEFT JOIN
(SELECT DISTINCT User FROM YourTable WHERE Status='C') CompletedAnything
ON a.User=CompletedAnything.User
WHERE COmpletedAnything.User IS NULL
Here's a SQL Fiddle that gives you what you want:
http://sqlfiddle.com/#!2/b6988/1
Query is this:
select User, Course, Status
from mytable
where User not in
(select distinct User from mytable where status = 'C' ans User is not null)

SQL - Removing Duplicate without 'hard' coding?

Heres my scenario.
I have a table with 3 rows I want to return within a stored procedure, rows are email, name and id. id must = 3 or 4 and email must only be per user as some have multiple entries.
I have a Select statement as follows
SELECT
DISTINCT email,
name,
id
from table
where
id = 3
or id = 4
Ok fairly simple but there are some users whose have entries that are both 3 and 4 so they appear twice, if they appear twice I want only those with ids of 4 remaining. I'll give another example below as its hard to explain.
Table -
Email Name Id
jimmy#domain.com jimmy 4
brian#domain.com brian 4
kevin#domain.com kevin 3
jimmy#domain.com jimmy 3
So in the above scenario I would want to ignore the jimmy with the id of 3, any way of doing this without hard coding?
Thanks
SELECT
email,
name,
max(id)
from table
where
id in( 3, 4 )
group by email, name
Is this what you want to achieve?
SELECT Email, Name, MAX(Id) FROM Table WHERE Id IN (3, 4) GROUP BY Email;
Sometimes using Having Count(*) > 1 may be useful to find duplicated records.
select * from table group by Email having count(*) > 1
or
select * from table group by Email having count(*) > 1 and id > 3.
The solution provided before with the select MAX(ID) from table sounds good for this case.
This maybe an alternative solution.
What RDMS are you using? This will return only one "Jimmy", using RANK():
SELECT A.email, A.name,A.id
FROM SO_Table A
INNER JOIN(
SELECT
email, name,id,RANK() OVER (Partition BY name ORDER BY ID DESC) AS COUNTER
FROM SO_Table B
) X ON X.ID = A.ID AND X.NAME = A.NAME
WHERE X.COUNTER = 1
Returns:
email name id
------------------------------
jimmy#domain.com jimmy 4
brian#domain.com brian 4
kevin#domain.com kevin 3