Beginner SQL query with ROW_NUMBER - sql

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.

Related

SQL Query to show results that don't have a relation to variable

For an assignment I have which includes a delete and add friend system (like Facebook), I've made a query that works by using two SQL tables, one which includes a friend_id, name and other information, and another which holds two friend_id columns, that show the relationship with the users and if they're friends.
User Table (friends)
| friend_id | profile_name |
|:---------- |:------------:|
| 1 | John |
| 2 | Peter |
| 3 | Alex |
| 4 | Nick |
---------------------------
Friendship Table (myfriends)
| friend_id1 | friend_id2 |
|:---------- |:----------:|
| 1 | 3 |
| 2 | 4 |
| 3 | 1 |
| 4 | 2 |
-------------------------
I am wanting to get a query which selects people that don't have a connection with a result (I want to show anyone who doesn't have a connection to friend_id '1', so only want to show users 2 and 4), and then display their name.
I have a query that selects the ones which have the relation which is:
SELECT friends.profile_name,friends.friend_id FROM `myfriends` JOIN `friends` ON friends.friend_id = myfriends.friend_id2 WHERE `friend_id1` = 1;
The query bellow shows all results from the table, and even using '!=', it doesn't select those who don't have a relation to friend_id '1'
SELECT friends.profile_name,friends.friend_id FROM `myfriends` JOIN `friends` ON friends.friend_id = myfriends.friend_id2 WHERE `friend_id1` != 1;
How can I fix this query so it shows all results but those connected to ‘friend_id1’ = 1
with connected as (SELECT friend_id,
myfriends.friend_id2 friend
FROM myfriends
JOIN friends
ON friends.friend_id = myfriends.friend_id1
WHERE friend_id1 = 1)
select *
from friends
where friend_id not in (select distinct friend from connected union all select distinct friend_id from connected)
you cannot change the where clause as it specifies which user you want to focus on.
So first get the users that are connected (in the first cte), and then select all users except those found in the first result of the connected users.
By the way, your example is misleading as it can be solved with a bug by doing something simple in the join.
edit
while it wasn't clease which version you were using, (I thought with clause is available in the newer mysql versions) I created another solution that is working on mysql 5.6 and should work for you as well:
select f.*
from friends f
left join (
SELECT friend_id, myfriends.friend_id2 friend
FROM myfriends
JOIN friends
ON friends.friend_id = myfriends.friend_id1
WHERE 1 in (friend_id,friend_id2)) f1
on f1.friend = f.friend_id
where f1.friend is null
it has a nicer implementation in one part (1 in one of 2 columns), and uses a left join that takes the nulls from the right table.

Problem with query to select distinct login

I need to select for each cod_user login that didn`t match to his login. Example: a -> acc (where in the table actually is abb).
I need it for some tests in data base in SoapUi.
I start with this, but can´t go any further for now:
SELECT U1.COD_USER, U2.LOGIN
FROM USERS U1
INNER JOIN USER U2
ON U1.LOGIN != U2.LOGIN
table name users
+----------+-------+
| cod_user | login |
+----------+-------+
| a | abb |
| b | acc |
| c | add |
| d | ahh |
| e | agg |
| f | ann |
+----------+-------+
But that query gives me all logins for each users that he didnt use and i only need one. Thanks you.
Does this do what you want?
SELECT U1.COD_USER,
MAX(U1.LOGIN) KEEP (DENSE_RANK FIRST ORDER BY DBMS_RANDOM.RANDOM) as UNUSED_LOGIN
FROM USERS U1
WHERE NOT EXISTS (SELECT 1
FROM USER U2
WHERE U1.COD_USER = U2.COD_USER AND
U1.LOGIN = U2.LOGIN
)
GROUP BY U1.COD_USER
In your both table, you must have a column which match. Like Cod_user must be common field and that needs to be used on join logic. So, just modify the your SQL like below
SELECT U1.COD_USER, U2.LOGIN
FROM USERS U1 INNER JOIN
USER U2
ON ( U1.COD_USER=U2.COD_USER and U1.LOGIN!= U2.LOGIN)
I understand that you want to assign to each user some random login belonging to other user from the same table and this assigned values should be distinct. So:
with t as (
select cod_user, login, count(1) over () cnt,
row_number() over (order by dbms_random.value) rn
from users )
select a.cod_user, a.login, b.login as random_login
from t a
left join t b on a.rn = b.rn + 1 or (a.rn = 1 and b.rn = b.cnt)
order by a.cod_user
dbfiddle demo
I assigned random row numbers to rows, then made self join on a.rn = b.rn + 1. First row must be joined exceptionally to the last, this is why count() over () was used. Probably you could also use mod() for this.
Assignment is random (due to dbms_random used for ordering) and unique. If you run this query severeal times you will get different, random, unique values.

Novice seeking help, Max Aggregate not returning expected results

I'm still very new to MS-SQL. I have a simple table and query that that is getting the best of me. I know it will something fundamental I'm overlooking.
I've changed the field names but the idea is the same.
So the idea is that every time someone signs up they get a RegID, Name, and Team. The names are unique, so for below yes John changed teams. And that's my trouble.
Football Table
+------------+----------+---------+
| Max_RegID | Name | Team |
+------------+----------+---------+
| 100 | John | Red |
| 101 | Bill | Blue |
| 102 | Tom | Green |
| 103 | John | Green |
+------------+----------+---------+
With the query at the bottom using the Max_RegID, I was expecting to get back only one record.
+------------+----------+---------+
| Max_RegID | Name | Team |
+------------+----------+---------+
| 103 | John | Green |
+------------+----------+---------+
Instead I get back below, Which seems to include Max_RegID but also for each team. What am I doing wrong?
+------------+----------+---------+
| Max_RegID | Name | Team |
+------------+----------+---------+
| 100 | John | Red |
| 103 | John | Green |
+------------+----------+---------+
My Query
SELECT
Max(Football.RegID) AS Max_RegID,
Football.Name,
Football.Team
FROM
Football
GROUP BY
Football.RegID,
Football.Name,
Football.Team
EDIT* Removed the WHERE statement
The reason you're getting the results that you are is because of the way you have your GROUP BY clause structured.
When you're using any aggregate function, MAX(X), SUM(X), COUNT(X), or what have you, you're telling the SQL engine that you want the aggregate value of column X for each unique combination of the columns listed in the GROUP BY clause.
In your query as written, you're grouping by all three of the columns in the table, telling the SQL engine that each tuple is unique. Therefore the query is returning ALL of the values, and you aren't actually getting the MAX of anything at all.
What you actually want in your results is the maximum RegID for each distinct value in the Name column and also the Team that goes along with that (RegID,Name) combination.
To accomplish that you need to find the MAX(ID) for each Name in an initial data set, and then use that list of RegIDs to add the values for Name and Team in a secondary data set.
Caveat (per comments from #HABO): This is premised on the assumption that RegID is a unique number (an IDENTITY column, value from a SEQUENCE, or something of that sort). If there are duplicate values, this will fail.
The most straight forward way to accomplish that is with a sub-query. The sub-query below gets your unique RegIDs, then joins to the original table to add the other values.
SELECT
f.RegID
,f.Name
,f.Team
FROM
Football AS f
JOIN
(--The sub-query, sq, gets the list of IDs
SELECT
MAX(f2.RegID) AS Max_RegID
FROM
Football AS f2
GROUP BY
f2.Name
) AS sq
ON
sq.Max_RegID = f.RegID;
EDIT: Sorry. I just re-read the question. To get just the single record for the MAX(RegID), just take the GROUP BY out of the sub-query, and you'll just get the current maximum value, which you can use to find the values in the rest of the columns.
SELECT
f.RegID
,f.Name
,f.Team
FROM
Football AS f
JOIN
(--The sub-query, sq, now gets the MAX ID
SELECT
MAX(f2.RegID) AS Max_RegID
FROM
Football AS f2
) AS sq
ON
sq.Max_RegID = f.RegID;
Use row_number()
select * from
(SELECT
Football.RegID AS Max_RegID,
Football.Name,
Football.Team, row_number() over(partition by name order by Football.RegID desc) as rn
FROM
Football
WHERE
Football.Name = 'John')a
where rn=1
simply you can edit your query below way
SELECT *
FROM
Football f
WHERE
f.Name = 'John' and
Max_RegID = (SELECT Max(Football.Max_RegID) where Football.Name = 'John'
)
or
if sql server simply use this
select top 1 * from Football f
where f.Name = 'John'
order by Max_RegID desc
or
if mysql then
select * from Football f
where f.Name = 'John'
order by Max_RegID desc
Limit 1
You need self join :
select f1.*
from Football f inner join
Football f1
on f1.name = f.name
where f.Max_RegID = 103;
After re-visit question, the sample data suggests me subquery :
select f.*
from Football f
where name = (select top (1) f1.name
from Football f1
order by f1.Max_RegID desc
);

How to have 'distinct' in having clause

EDIT: This is an example relation! I need it to work on a bigger relation so no workarounds!
So I was given a simple task and at first I didn't see what could possibly be wrong and now I just don't understand why it doesnt work.
Lets say I have a table of people and their friends and I want to select the ones who have 2 or more friends.
people
------------------------------
|person | friend | relation |
|-----------------------------
|ana | jon | friend |
|ana | jon | lover |
|ana | phillip| friend |
|ana | kiki | friend |
|mary | jannet | friend |
|mary | jannet | lover |
|peter | july | friend |
I would want to do a
SELECT person FROM people GROUP BY person HAVING count(distinct friend) > 1;
and get
-------
| ana |
-------
But I get a syntax error when using the 'distinct' in the HAVING clause.
I understand that the 'distinct' is a part of the projection clause but
how do I make 'count' only count distinct entries without an additional subquery or something?
EDIT: The best I could come up with is:
SELECT tmp.person FROM (SELECT person, count(distinct friend)
AS numfriends FROM people GROUP BY person) AS tmp
WHERE tmp.numfriends > 1;
From the doc
http://www-01.ibm.com/support/knowledgecenter/SSGU8G_12.1.0/com.ibm.sqls.doc/ids_sqs_0162.htm
The condition in the HAVING clause cannot include a DISTINCT or UNIQUE
aggregate expression.
An work-around would be to have the count distinct in the select
SELECT
person,
count(distinct friend) as f_count
FROM people
GROUP BY person
HAVING f_count > 1;
UPDATE :
Checked the document and found the facts
The HAVING clause is evaluated before the SELECT - so the server
doesn't yet know about that alias.
So to achieve the goal it could be done as
select
person,
f_count
from(
SELECT
person,
count(distinct friend) as f_count
FROM people
GROUP BY person
)x
where f_count > 1
you need to write it like this
SELECT person
FROM people
WHERE relation = 'friend'
GROUP BY person
HAVING count(*) > 1;
just check this.
declare #t table(person varchar(50), Friend VARCHAR(50), relation VARCHAR(50))
INSERT INTO #T VALUES('ana', 'jon','friend')
,('ana', 'jon','lover')
,('ana', 'phillip','friend')
,('ana', 'kiki','friend')
,('mary', 'jannat','friend')
,('mary', 'jannat','lover')
SELECT DISTINCT PERSON FROM
(
SELECT person FROM #t GROUP BY person HAVING count(friend) > 1
) a
Just an alternative way via using more group by's -
select cust_xref_id
from(
select cust_xref_id,cm11
from temp_nippon_cust
group by cust_xref_id,cm11
) temp
group by cust_xref_id
having count(cm11) > 1

Recursive SQL Server query

In a table reviewers with a structure like this:
reviewer | reviewee
===================
2 | 1
3 | 2
4 | 3
5 | 4
In a function call, I know both a reviewer-id and a reviewee-id (the owner of the item the reviewee is looking to retrieve).
I'm now trying to send a query that iterates all the entries in the reviewers table, starting with the reviewer, and ends at the reviewee's id (and matches that to the reviewee id I know). So I'm trying to find out if there is a connection between reviewee and reviewer at all.
Is it possible to do this in a single query?
You can do this:
WITH CTE
AS
(
SELECT reviewer, reviewee
FROM TableName
WHERE reviewee = #revieweeID
UNION ALL
SELECT p.reviewer, p.reviewee
FROM CTE c
INNER JOIN TableName p ON c.reviewee = p.reviewer
)
SELECT *
FROM CTE;
--- WHERE reviewer = #reviewerID;
Demo