SQL: get Nth item in each group - sql

I have a user table like this
user_id | community_id | registration_date
--------------------------------------------
1 | 1 | 2008-01-01
2 | 1 | 2008-05-01
3 | 2 | 2008-01-28
4 | 2 | 2008-07-22
5 | 3 | 2008-01-11
For each community, I would like to get the time that the 3rd user registered. I can easily do this for a single community using MySql's 'limit' SQL extension. For example, for community with ID=2
select registration_date
from user
order by registration_date
where community_id = 2
limit 2, 1
Alternatively, I can get the date that the first user registered for all communities via:
select community_id, min(registration_date)
from user
group by 1
But I can't figure out how to get the registration date of the 3rd user for all communities in a single SQL statement.
Cheers,
Don

With an inner select:
select
registration_date, community_id
from
user outer
where
user_id IN (
select
user_id
from
user inner
where
inner.community_id = outer.community_id
order by
registration_date
limit 2,1
)
order by registration_date
Selects the set of users where each user is the 3rd user in their community as returned by the limit clause in the inner select.

Is this what you mean?
SELECT registration_date
FROM user
ORDER BY registration_date
LIMIT n
Where n is the user you are concerned about.

Related

Show column values as comma seperated in grafana

I have a table with 2 columns
organization_id | user_name
1 | abc
1 | xyz
2 | bhi
2 | ipq
2 | sko
3 | ask
...
Each organization could have any number of users ranging from 1 to 100, 2000 and so on.
I wanted to show them in grafana in a table as following:
organization_id | user_name
1 | abc, xyz
2 | bhi, ipq, sko
3 | ask
Since there could be many users I want to show any 10 users belonging to same organization.
The database here is timescale db, the table is also a time series table showing when user was registered
If I understand rightly that you want 10 users per organisation you can use the query below.
I have added group by in the CTE to avoid returning duplicate user_name's.
In the test schema there are duplicate values of 'pqr' for organisation 2 but this username is only returned once even though there are less then 10 user_name's for 2
test schema db Fiddle here
With topTen as
(Select
Organisation_id,
User_name,
Rank() over (
partition by organisation_id
order by user_name) rn
From table_name
group by
Organisation_id,
user_name)
Select
Organisation_id,
String_agg(user_name,',') users
From topTen
Where rn <= 10
group by Organisation_id;
organisation_id | users
--------------: | :--------------------------------------
1 | abc,abk,def,ghi,jkl,mno,pqr,rst,ruk,stu
2 | abk,pqr,rst,ruk,stu,vwx
Another alternative which may be useful. If you remove the where and put the following after From topTen you will get all the distinct user_names, 10 per row.
group by Organisation_id,rn/10
order by Organisation_id,rn/10;
db<>fiddle here

Get users from two tables where id's are not the same

I ran across on a pretty though query for Rest API. Want to hear your recommendations.
Now I have two tables :
Company Users: which stores major information about the registered users like:
id, name, email, phone number.
Matching_users: which stores the information like:
active_user_id, matching_user_id where active user id is the authorized user ID, and matching user id is the any other user id which Authorized user have met before.
The question is: how I could execute the query where I would be able to fetch all other users (except authorized user ID, and users with whom authorized user would have met before )?
For example:
TABLE : company_users
ID | NAME | EMAIL
1 | Jake | jake#gmail.com
2 | Jane | jane#gmail.com
3 | Jacob | jacob#gmail.com
4 | June | june#gmail.com
and
TABLE : matching_table
ID | ACTIVE_USER_ID | MATCHING_USER_ID
1 | 1 | 2
How can I execute for authorized user only users from company_userswith id 3 and 4?
Get all rows of table 2 :
$matching_rows = MatchingTable::all();
$active_users = $matching_rows->pluck('active_user_id')->toArray();
$matching_users = $matching_rows->pluck('active_user_id')->toArray();
Merging them together :
$auth_users = array_merge($active_users, $matching_users);
Using whereNotIn :
$unauth_users = Users::whereNotIn('id', $auth_users)->get()`
Please try this.
If Except ACTIVE_USER_ID Then
Select * from company_users
Where Id NOT IN ( SELECT Distinct ACTIVE_USER_ID FROM matching_table)
If Except MATCHING_USER_ID Then
Select * from company_users
Where Id NOT IN ( SELECT Distinct MATCHING_USER_ID FROM matching_table)
If Except ACTIVE_USER_ID and MATCHING_USER_ID Then
Select * from company_users
Where Id NOT IN ( SELECT Distinct MATCHING_USER_ID FROM matching_table)
AND Id NOT IN ( SELECT Distinct ACTIVE_USER_ID FROM matching_table)

Beginner SQL query with ROW_NUMBER

i'm kind of a beginner with SQL.
Right now i'm trying to create a bit complex select but i'm getting some error, which I know it's a beginner mistake.
Any help appreciated.
SELECT ROW_NUMBER() OVER (ORDER BY score) AS rank, userID, facebookID, name, score FROM (
SELECT * FROM Friends AS FR WHERE userID = ?
JOIN
Users WHERE Users.facebookID = FR.facebookFriendID
)
UNION (
SELECT * FROM User WHERE userID = ?
)
Where the 2 ? will be replaced with my user's ID.
The table User contains every user in my db, while the Friends table contains all facebookFriends for a user.
USER TABLE
userID | facebookID | name | score
FRIENDS TABLE
userID | facebookFriendID
Sample data
USER
A | facebookID1 | Alex | 100
B | facebookID2 | Mike | 200
FRIENDS
A | facebookID2
A | facebookID3
B | facebookID1
I'd like this result since Alex and mike are friends:
rank | userID | facebookID | name
1 | B | facebookID2 | Mike
2 | A | facebookID1 | Alex
I hope this was quite clear explanation.
I'm getting this error at the moment:
Error occurred executing query: Incorrect syntax near the keyword 'AS'.
You've got several issues with your query. JOINS come before WHERE clauses. And when using a JOIN, you need to specify your ON clauses. Also when using a UNION, you need to make sure the same number of fields are returned in both queries.
Give this a try:
SELECT ROW_NUMBER() OVER (ORDER BY score) AS rank, userID, facebookID, name, score
FROM (
SELECT *
FROM Users
WHERE UserId = 'A'
UNION
SELECT U.userId, u.facebookId, u.name, u.score
FROM Friends FR
JOIN Users U ON U.facebookID = FR.facebookFriendID
WHERE FR.userID = 'A' ) t
SQL Fiddle Demo
Also, by the way your using ROW_NUMBER, it really will be a Row Number vs a RANK. If you want Rankings (with potential ties), replace ROW_NUMBER with RANK.

SELECT certain fields based on certain WHERE conditions

I am writing an advanced MySQL query that searches a database and retrieves user information. What I am wondering is can I select certain fields if WHERE condition 1 is met and select other fields if WHERE condition 2 is met?
Database: users
------------------------
| user_id | first_name |
------------------------
| 1 | John |
------------------------
| 2 | Chris |
------------------------
| 3 | Sam |
------------------------
| 4 | Megan |
------------------------
Database: friendship
--------------------------------------
| user_id_one | user_id_two | status |
--------------------------------------
| 2 | 4 | 0 |
--------------------------------------
| 4 | 1 | 1 |
--------------------------------------
Status 0 = Unconfirmed
Status 1 = Confirmed
OK, as you can see John & Megan are confirmed friends while Chris & Megan are friends but the relationship is unconfirmed.
The query I am trying to write is as follow: Megan(4) searches for new friends I want all of the users except for the ones she is a confirmed friend with to be returned. So, the results should return 2,3. But since a relationship with user_id 2 exists but is not confirmed, I want to also return the status since an entry in the friendship table does exist between the two. If a user exist but there is no connection in the relationship table it still returns that users information but returns status as a NULL or doesn't return status at all since it doesn't exist in that table.
I hope this makes since. Ask questions if you need to.
Why not use a left join or an if-not-exists?
SELECT users.*
FROM (users LEFT JOIN friendships
ON status=1 AND (user_id_one=user_id OR user_id_two=user_id) )
WHERE
status IS NULL
or
SELECT users.*
FROM users
WHERE
NOT EXISTS (SELECT *
FROM friendships
WHERE status=1
AND (user_id_one=user_id
OR user_id_two=user_id))
You can create to separate queries and then UNION the result tables. In each query, add a field that always has the same value.
So something like this should work:
(SELECT id, 'Not Friends' As Status FROM t1 WHERE condition1)
UNION
(SELECT id, 'Unconfirmed' As Status FROM t1 WHERE condition2)
Just make sure the same number and name of fields exists in both queries.

Need help with SQL query

The following is a simplified version of a database table I'm querying (let's call it Payments):
date | userid | payment
20/1/10 | 1 | 10
21/1/10 | 1 | 15
17/1/10 | 2 | 7
18/1/10 | 2 | 9
It records payments made by users on certain dates. I need to find out the details of the first payment made by each user like so:
20/1/10 | 1 | 10
17/1/10 | 2 | 7
Stored procedures are out of the question. Is there any way to do this using SQL alone or should I just add a first payment flag to the table?
SELECT MIN([Date]), userid, payment
FROM Payments
GROUP BY Userid, payment
SELECT MIN([Date]), UserID FROM Payments GROUP BY UserID
Try this:
SELECT * FROM Payments
INNER JOIN (SELECT Min([Date]) AS MinDate, UserID
FROM Payments GROUP BY UserID) AS M
ON M.MinDate = Payments.Date AND M.UserID = Payments.UserID