How to order by other table? - sql

I'm working on Stackoverflow-like system for my school project on ASPX. I'm already way beyond what the teacher thought us, so I decided to use the stupid way of counting down and upvotes - Having a spacial table for each vote.
Here are both of my tables:
comments
id | content | by | bestanswer
1 | demo | Vlad | 0
2 | sample | Hagay | 1
3 | chacking| Gil | 0
4 | trying | Teddy | 0
...| ... | ... | ...
votes
postid | username | upvote
1 | Gil | 1
1 | Hagay | 1
1 | Teddy | -1
2 | Gil | -1
3 | Vlad | -1
2 | Gil | 1
... | ... | ...
How can I, using SQL, SELECT all and set the one with bestanswer=1 as the first, and then order by the ammount of upvotes (2, 1, 3)?

You can aggregate the "upvotes" in a subquery, then JOIN to that table for ordering:
SELECT c.*,v.upvotes
FROM comments c
LEFT JOIN (SELECT postid,SUM(upvote) as upvotes
FROM votes
GROUP BY postid
) v
ON c.id = v.postid
ORDER BY c.bestanswer DESC, upvotes DESC
I'm assuming that id and postid relate to each other, and that you want to return all fields from the comments table, and maybe the total votes as well.

Related

SQL: How to find rows in one table that have no references to rows in another tables?

I have three tables: users, rooms, room_users.
Users can have many rooms and rooms as well can have many users, so this is many to many relationship.
users table:
+----+-----------+-----+
| id | name | age |
+----+-----------+-----+
| 1 | Christian | 19 |
| 2 | Ben | 36 |
| 3 | Robert | 52 |
| 4 | Monica | 25 |
| 5 | Alice | 26 |
| 6 | William | 18 |
+----+-----------+-----+
rooms table:
+----+----------+
| id | name |
+----+----------+
| 1 | College |
| 2 | Work |
| 3 | Football |
+----+----------+
And room_users table that represents relationship between users and rooms:
+---------+---------+
| user_id | room_id |
+---------+---------+
| 1 | 1 |
| 1 | 3 |
| 2 | 2 |
| 4 | 1 |
| 5 | 2 |
| 6 | 1 |
| 6 | 3 |
+---------+---------+
So, having these tables we can say that:
Christian(1) belongs to College(1) and Football(3) rooms.
Ben(2) belongs to Work(2) room.
Robert(3) does not belong to any room.
Monica(4) belongs to College(1) room.
Alice(5) belongs to Work(2) room.
William(1) belongs to College(1) and Football(3) rooms.
And now if I want to find users (ids) that does belong to Football room I should use this query:
SELECT user_id FROM room_users WHERE room_id = 3
Output for this query:
+---------+
| user_id |
+---------+
| 1 |
| 6 |
+---------+
This is correct, only Christian(1) and William(3) belong to Football room.
But how to find users that does NOT belong to Football room?
In this case, query must return 2, 3, 4 and 5 ids. That is, all IDs excluding IDs from the first query.
Is it possible to do it using LEFT JOIN?
As far as I know, it is more efficient way than using sub-queries.
Thanks in advance!
EDIT:
I've found a query that can solve the problem, but this query is VERY SLOW on large database:
SELECT users.id FROM users WHERE 0=(SELECT COUNT(*) FROM room_users WHERE user_id=users.id AND room_id=3);
Without correlated behavior, try something like this:
SELECT u.*
FROM users AS u
LEFT JOIN (
SELECT DISTINCT user_id FROM room_users WHERE room_id = 3
) AS v
ON v.user_id = u.id
WHERE v.user_id IS NULL
;
For performance issues, start by reviewing the explain/execution plan and use of indexes.
You could find those users that belong to the football room AND then exclude those using not in.
Also you can use a JOIN
SELECT u.*
FROM
users u
WHERE user_id NOT IN
(SELECT user_id FROM room_users WHERE room_id=3)
You are correct that this is possible to do with a left join.
SELECT
u.id
FROM
users u
LEFT JOIN room_users ur
ON u.id = ur.user_id
AND ur.room_id = 3
WHERE
ur.room_id is null;

Join Lookup from 1 table to multiple columns

How do I link 1 table with multiple columns in another table without using mutiple JOIN query?
Below is my scenario:
I have table User with ID and Name
User
+---------+------------+
| Id | Name |
+---------+------------+
| 1 | John |
| 2 | Mike |
| 3 | Charles |
+---------+------------+
And table Product with multiple columns, but just focus on 2 columns CreateBy And ModifiedBy
+------------+-----------+-------------+
| product_id | CreateBy | ModifiedBy |
+------------+-----------+-------------+
| 1 | 1 | 3 |
| 2 | 1 | 3 |
| 3 | 2 | 3 |
| 4 | 2 | 1 |
| 5 | 2 | 3 |
+------------+-----------+-------------+
With normal JOIN, i will need to do 2 JOIN:
SELECT p.Product_id,
u1.Name AS CreateByName,
u2.Name AS ModifiedByName
FROM Product p
JOIN USER user u1 ON p.CreateBy = u1.Id,
JOIN USER user u2 ON p.ModifiedBy = u2.Id
to come out result
+------------+---------------+-----------------+
| product_id | CreateByName | ModifiedByName |
+------------+---------------+-----------------+
| 1 | John | Charles |
| 2 | John | Charles |
| 3 | Mike | Charles |
| 4 | Mike | John |
| 5 | Mike | Charles |
+------------+---------------+-----------------+
How do i avoid that 2 times JOIN?
I'm using MS-SQL , but open to all SQL query for my own learning curious
Your current design/approach is acceptable, I think, and the need for two joins is a function of there being two user ID columns. Each of the two columns requires a separate join.
For fun, here is a table design which you may consider if you really want to have to perform only one join:
+------------+-----------+-------------+
| product_id | user_id | type |
+------------+-----------+-------------+
| 1 | 1 | created |
| 2 | 1 | created |
| 3 | 2 | created |
| 4 | 2 | created |
| 5 | 2 | created |
| 1 | 3 | modified |
| 2 | 3 | modified |
| 3 | 3 | modified |
| 4 | 1 | modified |
| 5 | 3 | modified |
+------------+-----------+-------------+
Now, you can get away with a just a single join followed by an aggregation:
SELECT
p.product_id,
MAX(CASE WHEN t.type = 'created' THEN u.Name END) AS CreateByName,
MAX(CASE WHEN t.type = 'modified' THEN u.Name END) AS ModifiedByName
FROM Product p
INNER JOIN user u
ON p.user_id = u.Id
GROUP BY
p.product_id;
Note that I don't recommend this approach at all. It is much cleaner to use your current approach and use two joins. Joins can fairly easily be optimized using one or more indices. The above aggregation approach would probably not perform as well as what you already have.
If you use natural keys instead of surrogates, you won't need to join at all.
I don't know how you tell your products apart in the real world, but for the example I will assume you have a UPC
CREATE TABLE User
(Name VARCHAR(20) PRIMARY KEY);
CREATE TABLE Product
(UPC CHAR(12) PRIMARY KEY,
CreatedBy VARCHAR(20) REFERENCES User(Name),
ModifiedBy VARCHAR(20) REFERENCES User(Name)
);
Now your query is a simple select, and you also enforce uniqueness of your user names as a bonus, and don't need additional indexes.
Try it...
HTH
Join is the best Approach, but if looking for alternate approach you can use Inline Query.
SELECT P.PRODUCT_ID,
(SELECT [NAME] FROM #USER WHERE ID = CREATED_BY) AS CREATED_BY,
(SELECT [NAME] FROM #USER WHERE ID = MODIFIED_BY) AS MODIFIED_BY
FROM #PRODUCT P
DEMO

how to bake in a record count in a sql query

I have a query that looks like this:
select id, extension, count(distinct(id)) from publicids group by id,extension;
This is what the results looks like:
id | extension | count
-------------+-------------------------+-------
18459154909 | 12333 | 1
18459154909 | 9891114 | 1
18459154919 | 43244 | 1
18459154919 | 8776232 | 1
18766145025 | 12311 | 1
18766145025 | 1122111 | 1
18766145201 | 12422 | 1
18766145201 | 14141 | 1
But what I really want is for the results to look like this:
id | extension | count
-------------+-------------------------+-------
18459154909 | 12333 | 2
18459154909 | 9891114 | 2
18459154919 | 43244 | 2
18459154919 | 8776232 | 2
18766145025 | 12311 | 2
18766145025 | 1122111 | 2
18766145201 | 12422 | 2
18766145201 | 14141 | 2
I'm trying to get the count field to show the total number of records that have the same id.
Any suggestions would be appreciated
I think you want to count distincts extentions, not ids.
Run this query:
select id
, extension
(select count(*) from publicids p1 where p.id = p1.id ) distinct_id_count
from publicids p
group by id,extension;
This is more or less the same as Pastor's answer. Depending on what the optimizer does it might be faster with higher record count source tables.
select p.id, p.extension, p2.id_count
from publicids p
inner join (
select id, count(*) as id_count
from publicids group by id
) as p2 on p.id = p2.id

Query returned with an extra column in sql -ms access

So I am wondering. I fell into an interesting suggestion from another developer. So i basically have two tables I join in a query and I want the resulting table from the query to have an extra column that comes from the table on from the joint.
Example:
#table A: contains rating of players, changes randomly at any date depending
#on drop of form from the players
PID| Rating | DateChange |
1 | 2 | 10-May-2014 |
1 | 4 | 20-May-2015 |
1 | 20 | 1-June-2015 |
2 | 4 | 1-April-2014|
3 | 4 | 5-April-2014|
2 | 3 | 3-May-2015 |
#Table B: contains match sheets. Every player has a different match sheet
#and plays different dates.
MsID | PID | MatchDate | Win |
1 | 2 | 10-May-2014 | No |
2 | 1 | 15-May-2015 | Yes |
3 | 3 | 10-Apr-2014 | No |
4 | 1 | 21-Apr-2015 | Yes |
5 | 1 | 3-June-2015 | Yes |
6 | 2 | 5-May-2015 | No |
#I am trying to achieve this by running the ms-access query: i want to get
#every players rating at the time the match was played not his current
#rating.
MsID | PID | MatchDate | Rating |
1 | 2 | 10-May-2014 | 4 |
2 | 1 | 15-May-2015 | 2 |
3 | 3 | 10-Apr-2014 | 4 |
4 | 1 | 21-Apr-2015 | 4 |
5 | 1 | 3-June-2015 | 20 |
6 | 2 | 5-May-2015 | 3 |
This is what I have tried below:
Select MsID, PID, MatchDate, A-table.rating as Rating from B-table
left Join A-table
on B-table.PID = A-table.PID
where B-table.MatchDate > A-table.Datechange;
any help is appreciated. The solution can be in Vba as long as it returns something like a view/table I can manipulate using other queries or report.
Think of this in terms of sets of data... you need a set that lists the MAX dateChange for each player's and match date.
Soo...
SELECT MAX(A.DateChange) MDC, A.PID, B.Matchdate
FROM B-table B
INNER Join A-table A
on B.PID = A.PID
and A.DateChange <= B.MatchDate
GROUP BY A.PID, B.Matchdate
Now we take this and join it back to what you've done to limit the results in table A and B to ONLY those with that date player and matchDate (my inline table C)
SELECT B.MsID, B.PID, B.MatchDate, A.rating as Rating
FROM [B-table] B
INNER JOIN [A-table] A
on B.PID = A.PID
INNER JOIN (
SELECT MAX(Y.DateChange) MDC, Y.PID, Z.Matchdate
FROM [B-table] Z
INNER Join [A-table] Y
on Z.PID = Y.PID
and Y.DateChange <= Z.MatchDate
GROUP BY Y.PID, Z.Matchdate) C
on C.mdc = A.DateChange
and A.PID = C.PId
and B.MatchDate = C.Matchdate
I didn't create a sample for this using your data so it's untested but I believe the logic is sound...
Now Tested! SQL Fiddle using SQL server though...
My results don't match yours exactly. I think you're expected results are wrong though for MSID 4 given rules defined.

PostgreSQL select all from one table and join count from table relation

I have two tables, post_categories and posts. I'm trying to select * from post_categories;, but also return a temporary column with the count for each time a post category is used on a post.
Posts
| id | name | post_category_id |
| 1 | test | 1 |
| 2 | nest | 1 |
| 3 | vest | 2 |
| 4 | zest | 3 |
Post Categories
| id | name |
| 1 | cat_1 |
| 2 | cat_2 |
| 3 | cat_3 |
Basically, I'm trying to do this without subqueries and with joins instead. Something like this, but in real psql.
select * from post_categories some-type-of-join posts, count(*)
Resulting in this, ideally.
| id | name | count |
| 1 | cat_1 | 2 |
| 2 | cat_2 | 1 |
| 3 | cat_3 | 1 |
Your help is greatly appreciated :D
You can use a derived table that contains the counts per post_category_id and left join it to the post_categories table
select p.*, coalesce(t1.p_count,0)
from post_categories p
left join (
select post_category_id, count(*) p_count
from posts
group by post_category_id
) t1 on t1.post_category_id = p.id
select post_categories.id, post_categories.name , count(posts.id)
from post_categories
inner join posts
on post_category_id = post_categories.id
group by post_categories.id, post_categories.name