Do these SQL queries make sense for the given tables? - sql

Give the following two SQL tables I want to run two queries (venue_id is a foreign key with id from the venues table):
SELECT venues.name FROM venues INNER JOIN users ON venues.venue_id = users.venue_id WHERE users.id = {param}
To return the venue name associated with the user_id that was passed in
SELECT users.name FROM users WHERE users.venue_id = {param] AND users.expiration_time > CURRENT_TIMESTAMP
To return all the current users with a given venue_id where the expiration_time is after the current time.
Do both these queries do what I expect them to do?
Thanks!

SELECT venues.name FROM venues INNER JOIN users ON venues.id = users.venue_id WHERE users.id = {param}
SELECT users.name FROM users WHERE users.venue_id = {param] AND users.expiration_time > CURRENT_TIMESTAMP
Both queries should be doing what you expect them to do

Related

Trying to count the number of occurences that 3 columns from 2 tables have on my organizations table? I need the occurrences joined in one table

-- 2. In one table, show how many private topics, admins, and standard users each organization has.
SELECT organizations.name, COUNT(topics.privacy) AS private_topic, COUNT(users.type) AS user_admin, COUNT(users.type) AS user_standard
FROM organizations
LEFT JOIN topics
ON organizations.id=topics.org_id
AND topics.privacy='private'
LEFT JOIN users
ON users.org_id=organizations.id
AND users.type='admin'
LEFT JOIN users
ON users.org_id=organizations.id
AND users.type='standard'
GROUP BY organizations.name
;
org_id is the foreign key that reals both the users table and topics table. It keeps giving me the wrong result by only either counting the number of admins or standard users and putting that for all rows in the each column. Any help is really appreciated as I have been stuck on this for a while now!
So, I am getting an error when I do as you said which is that the users table cannot be specified more than once. I updated the code to how you said to write it but still nothing. They really don't give me any sample data either but I just made some queries and saw the number of times there are private topics for example, which is in the privacy column of the topics table. When I dont get this error as I said, the joins seem to overwrite themselves where each row for all the columns is the same as the last join.
It appears to me that topics and users have no relationship. You're just trying to get the result together in a single query. There are other and possibly better ways to accomplish that but I think this will fix what you've got already (assuming you have id columns for each table.)
SELECT
organizations.name,
COUNT(DISTINCT topics.id) AS private_topic,
COUNT(DISTINCT users.id) FILTER (WHERE users.type = 'admin') AS user_admin,
COUNT(DISTINCT users.id) FILTER (WHERE users.type = 'standard') AS user_standard`
FROM organizations
LEFT JOIN topics
ON organizations.id = topics.org_id AND topics.privacy = 'private'
LEFT JOIN users
ON users.org_id = organizations.id
GROUP BY organizations.name;
I propose this as a more straightforward way:
SELECT
min(o.name) as "name",
(
select count(*) from topics t
where t.org_id = o.id AND t.privacy = 'private'
) as private_topics,
(
select count(*) from users u
where u.org_id = o.id and u.type = 'admin'
) AS user_admin,
(
select count(*) from users u
where u.org_id = o.id and u.type = 'standard'
) AS user_standard
FROM organizations o
GROUP BY o.id;

Best way to get distinct count from a query joining two tables (multiple join possibilities)

I have 2 tables, table Actions & table Users. Actions -> Users is many-one association.
Table Actions (has thousands of rows)
id
uuid
name
type
created_by
org_id
Table Users (has a max of hundred rows)
id
username
org_id
org_name
I am trying to get the best join query to obtain a count with a WHERE clause. I need the count of distinct created_bys from table Actions with an org_name in Table Users that contains 'myorg'. Also, (Actions.created_by = Users.username)
I currently have the below queries (producing expected results) and wonder which is better and if it can be optimized further?
Query 1:
select count(distinct Actions.created_by)
from Actions join
Users
on Users.org_id = Actions.org_id
where Users.org_name like '%myorg%';
Query 2:
select count(distinct Users.username)
from Users join
Actions
on Actions.created_by = Users.username
where Users.org_name like '%myorg%';
The fastest approach is to modify the second query:
select count(*)
from Users u
where exists (select 1
from Actions a
where a.created_by = u.username
)
and u.org_name like '%myorg%';
Then the best index is on actions(created_by).

Select creating a Column with results of another query as a JSON

I'm trying to create a query that will fetch results from table parties. This table contains two foreign keys and I'm having trouble "mapping" these foreign keys.
For the first foreign key I need to map my host_id column to the actual name of the person users.name.
I was able to solve this with:
SELECT parties.*, users.name as host_name
FROM parties
INNER JOIN users ON parties.host_id = users.id
My second foreign key is to a table called guests which has a FK named party_refer which refers to parties.id.
The following query includes my Guests as part of the results (by appending all of my guests table columns in the results)
SELECT parties.*, users.name as host_name, guests.*
FROM parties
INNER JOIN users ON parties.host_id = users.id
INNER JOIN guests ON parties.id = guests.party_refer
I would like to modify this second INNER JOIN so that the results of (select * from guests) are returned as a single Column called Guests with the results expressed as a JSON.
I believe I need to use array_to_json(array_agg(row_to_json())) but I've been trying for hours to get it working with no luck.
I think you are looking for
SELECT parties.*, users.name as host_name, json_agg(row_to_json(guests)) as guests
FROM parties
INNER JOIN users ON parties.host_id = users.id
INNER JOIN guests ON parties.id = guests.party_refer
GROUP BY parties.id, users.name
Although a subquery may be simpler than extensive grouping:
SELECT
parties.*,
users.name as host_name,
(SELECT json_agg(row_to_json(guests))
FROM guests
WHERE guests.party_refer = parties.id) as guests
FROM parties
INNER JOIN users ON parties.host_id = users.id
(online demo)
You might prefer an explicit json_build_object instead of the row_to_json, e.g.
json_agg(json_build_object('guestName', guests.name))

Is it right to look at this query as the count of elements in an intersection?

A bit new to SQL and I have a Postgresql query and it's my understanding in Postgres 9.x that JOIN by default is INNER JOIN:
select count(*)
from physical_promotions
JOIN subscriptions ON physical_promotions.product_id = subscriptions.id
JOIN users on subscriptions.user_id = users.id
JOIN referrals ON referrals.referred_user_id = users.id
where physical_promotions.coupon_id = '55';
Is the right way to interpret this as the count of all physical promotions with a coupon_id of 55 intersected with a corresponding subscription intersected with a corresponding referral intersected with a corresponding user?
EDIT:
Also, I'm trying to count the number of physical_promotions - so is that the right way to go about it?
Here is my new query:
select count(distinct physical_promotions.id)
from physical_promotions
JOIN subscriptions ON physical_promotions.product_id = subscriptions.id
JOIN users on subscriptions.user_id = users.id
JOIN referrals ON referrals.referred_user_id = users.id
where physical_promotions.coupon_id = '55';
Yes, in fact, you may also write the same thing as follows:
SELECT count(*)
FROM physical_promotions,
subscriptions,
users,
referrals
WHERE physical_promotions.product_id = subscriptions.id
AND subscriptions.user_id = users.id
AND referrals.referred_user_id = users.id
AND physical_promotions.coupon_id = 55
For clarity I would probably recommend explicitly specifying the JOIN type.
Also note that you may not necessarily get a count of unique promotions. For example, if there are 2 subscriptions for one promotion, your results will include that promotion (and user and referral) twice.

Is it true that JOINS can be used everywhere to replace Subqueries in SQL

I heard people saying that table joins can be used everywhere to replace sub-queries. I tested it in my query, but found that appropriate data set was only retrieved when I used sub-queries. I was not able to get same data set using joins. I am not sure if what I found is right because I am a newcomer in RDBMS, thus not so much experienced. I will try to draw the schema (in words) of the database in which I was experimenting:
The database has two tables:
Users (ID, Name, City) and Friendship (ID, Friend_ID)
Goal: Users table is designed to store simple user data and Friendship table represents Friendship between users. Friendship table has both the columns as foreign keys, referencing to Users.ID. Tables have many-to-many relationship between them.
Question: I have to retrieve Users.ID and Users.Name of all the Users, which are not friends with a particular user x, but are from same city (much like fb's friend suggestion system).
By using subquery, I am able to achieve this. Query looks like:
SELECT ID, NAME
FROM USERS AS U
WHERE U.ID NOT IN (SELECT FRIENDS_ID
FROM FRIENDSHIP,
USERS
WHERE USERS.ID = FRIENDSHIP.ID AND USERS.ID = x)
AND U.ID != x AND CITY LIKE '% A_CITY%';
Example entries:
Users
Id = 1 Name = Jon City = Mumbai
Id=2 Name=Doe City=Mumbai
Id=3 Name=Arun City=Mumbai
Id=4 Name=Prakash City=Delhi
Friendship
Id= 1 Friends_Id = 2
Id = 2 Friends_Id=1
Id = 2 Friends_Id = 3
Id = 3 Friends_Id = 2
Can I get the same data set in a single query by performing joins. How? Please let me know if my question is not clear. Thanks.
Note: I used inner join in the sub-query by specifying both tables: Friendship, Users. Omitting the Users table and using the U from outside, gives an error (But if not using alias for the table Users, query becomes syntactically okay but result from this query includes ID's and names of users, who have more than one friends, including the user having ID x. Interesting, but is not the topic of the question).
For not in you can use left join and check for is null:
select u.id, u.name
from Users u
left join Friends f on u.id = f.id and f.friend_id = #person
where u.city like '%city%' and f.friend_id is null and u.id <> #person;
There are some cases where you can't work out your way with just inner/left/right joins, but your case is not one of them.
Please check sql fiddle: http://sqlfiddle.com/#!9/1c5b1/14
Also about your note: What you tried to do can be achieved with lateral join or cross apply depending on the engine you are using.
You can rewrite your query using only joins. The trick is to join to the User tables once with an inner join to identify users within the same city and reference the Friendship table with a left join and a null check to identify non-friends.
SELECT
U1.ID,
U1.Name
FROM
USERS U1
INNER JOIN
USERS U2
ON
U1.CITY = U2.CITY
LEFT JOIN
FRIENDSHIP F
ON
U2.ID = F.ID AND
U1.ID = F.FRIEND_ID
WHERE
U2.id = X AND
U1.ID <> U2.id AND
F.id IS NULL
The above query doesn't handle the situation where USER x's primary key is in the FRIEND_ID column of the FRIENDSHIP table. I assume because your subquery version doesn't handle that situation, perhaps you create 2 rows for each friendship, or friendships are not bi-directional.
Joins and subqueries can be used to achieve similar results in some cases, but certainly not all. As an example, this query with a subquery could not be achieve vis-a-vis a join:
SELECT ID, COLUMN1, COUNT(*) FROM MYTABLE
WHERE ID IN (
SELECT DISTINCT ID FROM MYTABLE
WHERE COLUMN2 NOT IN (VALUES1, VALUES2)
)
GROUP BY ID;
This is only one example, but there are many.
Conversely, you cannot get information from another table by using a subquery without joining it.
As to your example
SELECT ID, NAME FROM USERS AS U
WHERE U.ID NOT IN (
SELECT FRIENDS_ID FROM FRIENDSHIP, USERS
WHERE USERS.ID = FRIENDSHIP.ID AND USERS.ID = x)
AND U.ID != x AND CITY LIKE '% A_CITY%';
This could be constructed as:
select ID, NAME from users u
join FRIENDSHIP f on f.ID = u.ID
where u.ID = x
and u.ID != y
and CITY like '%A_CITY';
I changed your second x to a y assumptively, so it wouldn't cause confusion.
Of course, you may also want to LEFT JOIN aka LEFT OUTER JOIN if there is a chance that there may be multiple results in the FRIENDSHIP table.