Recursive SQL Server query - sql

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

Related

Joining a large number of tables so that all dates are kept

I have around 50-70 tables that look very similar, say:
Table 1:
id | date | count_A | count_B
1 12.05.2021 12 15
Table 2:
id | date | count_A | count_B
1 15.05.2021 8 24
The main table looks like the following:
id | label
1 X
In the end, what I would like to get is:
id | date | count_A | count_B | label
1 12.05.2021 12 15 X
1 15.05.2021 8 24 X
One intuitive approach is to use the full outer join and join on id but that would result in strange rows with several date values.
Joining on (id, date) doesn't seem to be a great option either.
What can be a possible solution here? Thanks!
You can use a subquery with the statement WITH. Inside this subquery, you can use the UNION with all the tables with the same schema.
Use a join statement between the subquery, in this case tablaC and the main table, which has a different schema.
You can see this example:
WITH tablaC AS (
SELECT ID,date,count_C,Count_D FROM Table_C
UNION ALL
SELECT ID,date,count_C,Count_D FROM Table_D
)
select c.ID,date,c.count_C,c.Count_D,m.label
from tablaC as c
join table_main as m on c.id=m.id

Getting results from two different tables using a join

Let's say I have the following tables:
+-------------------------------------------+
| t_classroom |
+-------------------------------------------+
| PK | id |
| | admin_user_id |
| | name |
| | students |
+-------------------------------------------+
+-------------------------------------------+
| t_shared |
+-------------------------------------------+
| | admin_user_id |
| | classroom_id |
| | expiry |
+-------------------------------------------+
I want to write a query that will pull all classrooms that an admin_user_id has access to. In essence, I want a union of classroom rows when I search by admin_user_id in the t_classroom table as well as classroom rows when I search by admin_user_id in the t_shared table. I made the following attempt:
SELECT
id,
admin_user_id,
name,
students
FROM
t_classroom
WHERE
admin_user_id = 1
UNION ALL
SELECT
c.id,
c.admin_user_id,
c.name,
students
FROM
t_classroom c
INNER JOIN t_shared s
ON c.id = s.classroom_id
WHERE
admin_user_id = 1
Does the above look correct? Is there anything more efficient/cleaner?
Depending on how much data you have you could probably get away with just using an IN clause to look at the other table.
SELECT
c.id,
c.admin_user_id,
c.name,
c.students
FROM
t_classroom c
WHERE
c.admin_user_id = 1
OR c.id IN ( select s.classroom_id from t_shared s where s.admin_user_id = 1 )
Your union wont work because you're left-joining to the t_shared table and checking only the classroom admin user.
If you join the shared room you would also end up with duplicates and would need to distinct the result too.
Edit:
Because of the large number of rows it might be better to use an exists check on the 2nd table.
SELECT
c.id,
c.admin_user_id,
c.name,
c.students
FROM
t_classroom c
WHERE
c.admin_user_id = 1
OR EXISTS ( select 1 from t_shared s where s.classroom_id = c.id AND s.admin_user_id = 1 )
Your solution is fundamentally fine, the only two problems I can detect when eyeballing your query are:
You need to write s.admin_user_id instead of admin_user_id in the last line to avoid an error message, because there is a column of that name in both tables. Best practice is to always qualify column names with the table names.
You might want to use UNION instead of UNION ALL if you want to avoid a duplicate result row in the case that both tables have admin_user_id = 1 for the same classroom.

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.

SQL many-to-many select help needed

I have 2 tables
Bid_customer
|bidkey | customerkey
| 1 | 1
| 1 | 2
| 1 | 3
customer_groups
| groupkey | customerkey
| 1 | 1
| 1 | 2
| 1 | 3
What I'm trying to get is a result that will look like
| bidkey | groupkey
| 1 | 1
I've tried a cursor and joins but just don't seem to be able to get what i need any ideas or suggestions
EDIT: customers can belong to more that one group also
I am not sure who meaningful your sample data is. However following is a simple example.
Query:
select distinct b.bidkey, g.gkey
from bidcus b
inner join cusgroup g
on
b.cuskey = g.cuskey
and g.gkey = 10;
Results:
BIDKEY GKEY
1 10
Reference: SQLFIDDLE
In order to have a working Many-to-Many relationship in a database you need to have an intermediary table that defines the relationship so you do not get duplicates or mismatched values.
This select statement will join all bids with all groups because the customer matches.
Select bidkey, groupkey
From customer_groups
Inner Join bid_customer
Where customer_groups.customerkey = Bid_customer.customerkey
Hers is a sample Many to Many Relationship:
For your question:
You will need another table that joins the data. For example, GroupBids
customer_groups and bid_customer would have a one-to-many relationship with GroupBids
You would then do the following select to get your data.
Select bidkey, groupkey
From bid_customer
inner join GroupBids
ON bid_customer.primarykey = GroupBids.idBidKey
inner join customer_groups
ON customer_groups.primarykey = GroupBids.idCustomerGroupkey
This would make sure only related groups and bids are returned

Remove duplicates when joining tables

I have a news table as follows
News:
| id | title | description
| 1 | Breaking news | bla bla bla
| 2 | Heavy snowfall in london | bla bla bla
a Type table as follows:
| id | type_name | type_code
| 1 | weather | 0567
| 2 | city | 0653
and a NewsType table as follows
|id | news_id | type_id | created_by |
| 1 | 2 | 1 | "John" |
| 2 | 2 | 2 | "Alex" |
As you can see from the NewsType table that a single news can fall into two or more types.
I need to display news corresponding to types. A user might say give me all the news about cities and weather. To display this I am doing something like:
select distinct n.* , nt.created_at
from news n, newstype nt, type t where
n.id = nt.news_id and
t.id = nt.type_id
order by nt.created_at
limit 25
The problem is this query returns the same news twice (I think it's because of the inner join I am doing). What should I change in the query so that if a news is classified as two types, and the user has requested to view the same two types of news, I get only single news item? instead of two!
simple solution:
select * from news where news_id in (
select news_id
from NewsType
where type_id in (the types you want)
)
most people would say that you should add a DISTINCT on the news_id on the inner query. You can try that, but Im quite sure it will decrese performance.
Over all, if you think this solution doesnt perform well, you can make the inner query a CTE, which usually behaves better:
with my_CTE as(
select news_id
from NewsType
where type_id in (the types you want)
)
select *
from news
where news_id in (select news_id from my_CTE)
A group by is another approach to this:
select n.id, n.title, n.description, max(nt.created_at)
from news n, newstype nt, type t where
n.id = nt.news_id and
t.id = nt.type_id
group by n.id, n.title, n.description
order by nt.created_at
limit 25
Try
select distinct n.id, n.title, n.description
but, as #Jan Dvorak stated,
select distinct n.*
shouldn't select the same news twice
You want to select all of the stories that have an entry in the NewsType table for a praticular type. Therefore you want to select the news items where a relationship to the type exists:
SELECT
News.ID,
News.Title,
News.Description
FROM
News
WHERE
EXISTS
(SELECT
NULL
FROM
NewsType
INNER JOIN Type ON NewsType.Type_ID = Type.ID
WHERE
News.ID = NewsType.News_ID
AND Type.Type_Code = #typeCode)
The last line of the where clause may need to be changed to Type.Type_Name = #typeName if you are using the type name as the parameter
You need to decide what to do with the "duplicate" types: Do you want to display just one type for a news item associated with multiple types, or do you want to list them all?
If the latter, you could investigate using the string_agg function, see http://www.postgresql.org/docs/9.2/static/functions-aggregate.html
select distinct n.id, n.title, n.description, string_agg(t.type_name, ',')
from news n, newstype nt, type t where
n.id = nt.news_id and
t.id = nt.type_id
group by n.id, n.title, n.description
limit 25