SQL ORDER BY GROUP BY - sql

I have 2 tables (ENTRIES and USERS) linked by id, I want to select all the entries from the top 5 users only, and then order them by the COUNT of individual users entries.
USERS ENTRIES
tom entry1
mary entry2
jane entry3
tom entry4
tom entry5
and so on....
to display as
USERS ENTRIES
tom entry1
tom entry4
tom entry5
jane entry3
mary entry2
my code at the moment just orders by names but does not list by COUNT of entries
SELECT ENTRIES.entry, USERS.NAME FROM ENTRIES, USERS
WHERE ENTRIES.USER_ID = USERS.ID
ORDER BY USERS.NAME

Since you are using Oracle you can use windowing functions to get the total count for each user:
SELECT e.entry,
u.NAME,
count(*) over(partition by u.name) Total
FROM ENTRIES e
INNER JOIN USERS u
ON e.USER_ID = u.ID
ORDER BY total desc, u.NAME
See SQL Fiddle with Demo.
If you then want to return only the top 5 rows, then you can use:
select entry,
name,
total
from
(
SELECT e.entry,
u.NAME,
count(*) over(partition by u.name) Total,
rownum rn
FROM ENTRIES e
INNER JOIN USERS u
ON e.USER_ID = u.ID
)
where rn <= 5
ORDER BY total desc, NAME
See SQL Fiddle with Demo

Related

SQL Server | Select all specific duplicate in columns

My problem right now is that I need to make an statement where select all rows that are duplicates with specific needs.
For example I got table 1 (users):
Users:
----------------------------------------------
ID name lastname birth file number
1 Max Lix 2015-02-01 D43-892
2 Chris Maura 2010-12-25 E33-722
4 Lena Paul 2005-05-11 S85-458
5 Max Lix 2019-02-01 D23-992
6 Lena Paul 2005-05-11 S84-488
7 Lena Paul 2005-05-11 S75-258
----------------------------------------------
Address(u_ID = ID of Users table):
----------------------------------------------
ID u_ID Street number zip
1 1 Heystr. 12 4556
2 2 Nostr. 2 8978
3 4 Yesstr. 8a 2545
I need to get all rows where the name, lastname and birth does match with other rows and also get the address for that person.
The result should look like this:
Result:
----------------------------------------------
name lastname birth filenumber address
Max Lix 2015-02-01 D43-892 Heystr. 12 4556
Max Lix 2019-02-01 D23-992 Heystr. 12 4556
Lena Paul 2005-05-11 S85-458 Yesstr. 8a 2545
Lena Paul 2005-05-11 S84-488 Yesstr. 8a 2545
Lena Paul 2005-05-11 S75-258 Yesstr. 8a 2545
The first idea that I had was to use GROUP BY and HAVING but that does only return one row but I need every single duplicate matching the name, lastname and birth.
Use this:
select u.name, u.lastname, u.birth, u.filenumber, concat(a.street, ' ', a.number, ' ', a.zip) address
from users u
left join address a
on a.u_id = u.id
where
exists (
select 1 from users
where users.name = u.name and user.lastname = u.lastname and user.birth = u.birth and users.id <> u.id
)
With the condition:
users.name = u.name and user.lastname = u.lastname and user.birth = u.birth and users.id <> u.id
you can find the dupilcates.
Use inner join instead of left join if you want the duplicates only once.
SELECT name, lastname, birth, filenumber, concat(street,' ' , number, ' ', zip) as address
FROM Users A, Adress
WHERE u_id = a.id
AND (SELECT COUNT(1)
FROM Users B
WHERE A.name = B.name
AND A.lastname = B.lastname) > 1
with duplicate as ( -- this CTE makes a list of duplicated user_IDs
Select u.id
from users
group by name, lastname, birth
having count(*) >= 2
)
Select concat(street,' ' , number, ' ', zip) as address,
name, lastname, birth, filenumber
from duplicates d -- gather the data
join addresses on d.id=uid
join users on u.id=d.uid
will return you a report of all the people having an homonym with same birthday
Please try below,
select U.name,U.lastname,U.birth,U.filenumber,concat(street,' ',number,' ',zip) as address
from Users U join Address A on
U.ID=A.u_Id
group by name,lastname,birth
having count(*)>1

Pulling Datetime when using COUNT(UserID)

I have the tables Users
UserID FirstName LastName Email
------ --------- -------- -----
1 Fred Smith fs#abc.com
2 Bob Hill bh#abc.com
3 Jane Doe jd#abc.com
and LoginSession
LoginSessionID UserID StartDate
-------------- ------ ---------
1 1 2014-11-23 08:37:14.836
2 1 2014-11-25 11:13:53.225
3 2 2014-12-01 03:15:33.846
4 1 2014-12-01 17:34:19.036
5 3 2014-12-05 12:55:01.998
6 1 2014-12-14 17:20:14.636
7 3 2014-12-15 10:02:17.376
What I am trying to do is find the users who have logged on only once and find out when that was.
I have managed to find the users who have logged on only once by using
SELECT
U.FirstName, U.LastName, COUNT(L.UserID) AS Visits
FROM
LoginSession L
JOIN
Users U ON U.UserID = L.UserID
GROUP BY
U.FirstName, U.LastName
HAVING
COUNT(L.UserID) = 1
But I also want to pull through the L.StartDate of those users. If I add it to the select query I get an error because it's not contained in an aggregate function or GROUP BY clause. If I add it the the GROUP BY line (to avoid that error) I get each and every login handily marked as 1 visit!
I also tried using a subquery but I got an error because it returned more than one result.
I really am totally stumped!
You can do this with aggregation:
select UserId, min(StartDate) as StartDate
from LoginSession ls
group by UserId
having count(*) = 1;
The min() returns the value you want, because there is only one row that matches. You can use an addition join to get additional information about the users.
select u.*, lsu.StartDate
from Users u join
(select UserId, min(StartDate) as StartDate
from LoginSession ls
group by UserId
having count(*) = 1
) lsu
on lsu.UserId = u.UserId;
You can use windowed version of COUNT:
SELECT FirstName, LastName, StartDate
FROM (
SELECT U.FirstName, U.LastName, L.StartDate,
COUNT(*) OVER (PARTITION BY U.UserID) AS cnt
FROM LoginSession L
JOIN Users U ON U.UserID = L.UserID ) AS t
WHERE t.cnt = 1
COUNT with OVER clause will return the number of records per U.UserID. Using an outer query you can fetch exactly these records.
Demo here

SQL Server - select min date and id from foreign key

These are my tables:
USER:
id_user name email last_access id_company
1 jhonatan abc#abc.com 2014-12-15 1
2 cesar cef#cef.com 2014-12-31 1
3 john 123#123.com 2015-01-09 2
4 steven 897#asdd.cpom 2015-01-02 2
5 greg sd#touch.com 2014-12-07 1
6 kyle fb#fb.com 2014-11-20 1
COMPANY:
id_company company
1 Facebook
2 Appslovers
I need to know, what are the users which has the MIN last_access per company (just one). It could be like this:
id_user name last_access company
6 kyle 2014-11-20 Facebook
4 steven 2015-01-02 Appslovers
Is it possible ?
Use window function
SELECT id_user,
NAME,
last_access,
company
FROM (SELECT id_user,
NAME,
last_access,
company,
Row_number()OVER(partition BY company ORDER BY last_access) rn
FROM users u
JOIN company c
ON u.id_company = c.id_company) a
WHERE rn = 1
or join both the tables find the min last_access date per company then join the result back to the users table to get the result
SELECT id_user,
NAME,
a.last_access,
a.company
FROM users u
JOIN(SELECT u.id_company,
Min(last_access) last_access,
company
FROM users u
JOIN company c
ON u.id_company = c.id_company
GROUP BY u.id_company,
company) a
ON a.id_company = u.id_company
AND u.last_access = a.last_access
This can be done in many ways, for example by using a window function like row_number to partition the data and then selecting the top rows from each group like this:
;with cte (id_user, name, last_access, company, seq) as (
select
id_user,
name,
last_access,
company,
seq = row_number() over (partition by u.id_company order by last_access)
from [user] u
inner join [company] c on u.id_company = c.id_company
)
select id_user, name, last_access, company
from cte where seq = 1

ORA-01427: single-row subquery returns more than one row error

I have 2 tables (USERS and ACCOUNTS) with the following data in them:
USERS
UserID Name Account_Number
10 John Smith 13
20 Alex Brown 14
30 Mary Wade 34
ACCOUNTS
Account number Amount
13 40
34 30
14 30
13 60
14 10
I would like to know how I can write a query to return the following results:
UserID Name Total amount
13 John Smith 100
14 Alex Brown 40
34 Mary Wade 30
The query that I have tried is:
SELECT USER_ID, NAME, (SELECT SUM(AMOUNT) FROM ACCOUNTS GROUP BY ACCOUNT) AS TOTAL_AMOUNT
FROM USERS
JOIN ACCOUNTS
USING(ACCOUNT_NUMBER)
ORDER BY TOTAL_AMOUNT DESC;
When I execute this I get the following error: ORA-01427: single-row subquery returns more than one row.
Does anyone know how I might be able to modify the query so that it works as intended?
Thanks!
Please try:
select
Account_Number,
Name,
(select SUM(Amount) from ACOUNTS b where b.[Account number]=a.Account_Number) Total
from USERS a
order by Account_Number
Maybe something like this:
SELECT
USERS.USER_ID,
USERS.NAME,
SUM(ACCOUNTS.Amount) AS TotalAmout
FROM USERS
JOIN ACCOUNTS
ON ACCOUNTS.Account_number=USERS.Account_number
GROUP BY
USERS.USER_ID,
USERS.NAME
ORDER BY
TotalAmout DESC
Remove your subquery.. It is returning more than one row..
SELECT U.USERID, U.NAME, SUM(A.AMOUNT) AS TOTAL_AMOUNT
FROM USERS U
INNER JOIN ACCOUNT A on U.ACCOUNT_NUMBER=A.ACCOUNT_NUMBER
GROUP BY A.ACCOUNT_NUMBER,U.USERID, U.NAME
ORDER BY SUM(A.AMOUNT) DESC;
Fiddle

How to rank users and get a subset from this rank with my user and the above and below user by rank position

I am working on a query right now to get a ranking of my users. I have two tables one for users and the other one for profits where I save the amount and the user id to which is related. By getting the total of profits generated by a user I need to build a rank with three users, the user in the next higher ranked position to my user, my user and the user in the next lower ranked position to my user. For example:
id | name | total_profit | rank
-------+-----------------------------+--------------+------
10312 | John Doe | 7000.0 | 1
10329 | Michael Jordan | 5000.0 | 2
10333 | Kobe Bryant | 4000.0 | 3
10327 | Mike Bibby | 4000.0 | 3
10331 | Phil Jackson | 1000.0 | 4
In this if my user is Kobe Bryant I would need to get a rank with Michael Jordan, Kobe Bryant and Phil Jackson.
If my user is Mike Bibby I would need to get a rank with Michale Jordan, Mike Bybby and Phil Jackson.
Until now I have a query that returns me a full rank with all the users but I do not now what is a good way to get the three users that I want. I have tried to do this with ruby but I think it would be better if I do all this processing in the DB.
SELECT users.id, users.name, total_profit, rank() OVER(ORDER BY total_profit DESC)
FROM users
INNER JOIN (SELECT sum(profits.amount) AS total_profit, investor_id
FROM profits GROUP BY profits.investor_id) profits ON profits.investor_id = users.id
ORDER BY total_profit DESC;
I am using PostgresSQL 9.1.4
with s as (
select
users.id, users.name, total_profit,
rank() over(order by total_profit desc) as r
from
users
inner join
(
select sum(profits.amount) as total_profit,
investor_id
from profits
group by profits.investor_id
) profits on profits.investor_id = users.id
), u as (
select r from s where name = 'Kobe Bryant'
)
select distinct on (r) id, name, total_profit, r
from s
where
name = 'Kobe Bryant'
or r in (
(select r from u) - 1, (select r from u) + 1
)
order by r;
with cte_profits as (
select
sum(p.amount) as total_profit, p.investor_id
from profits as p
group by p.investor_id
), cte_users_profits as (
select
u.id, u.name, p.toral_profit,
dense_rank() over(order by p.total_profit desc) as rnk,
row_number() over(partition by up.total_profit order by up.id) as rn
from users as u
inner join cte_profits as p on p.investor_id = u.id
)
select c2.*
from cte as c
left outer join cte as c2 on
c2.id = c.id or
c2.rnk = c.rnk + 1 and c2.rn = 1 or
c2.rnk = c.rnk - 1 and c2.rn = 1
where c.name = 'Kobe Bryant'
order by c2.rnk
sql fiddle demo