How to combine these two complicated queries into one query without union? - sql

(SELECT posts.id FROM posts
INNER JOIN discussions ON discussions.post_id = posts.id
INNER JOIN companies ON discussions.company_id = companies.id
INNER JOIN subscriptions ON subscriptions.subscribable_id = companies.id AND subscriptions.subscribable_type = 'Company'
INNER JOIN users ON subscriptions.user_id = users.id WHERE users.id = 6)
UNION
(SELECT posts.id FROM posts
INNER JOIN users users_2 ON posts.analyst_id = users_2.id
INNER JOIN subscriptions ON subscriptions.subscribable_id = users_2.id AND subscriptions.subscribable_type = 'User'
INNER JOIN users ON subscriptions.user_id = users.id WHERE users.id = 6)
It should be obvious that the last join is the same in both queries.. Just not sure how to "or" together joins.

Some joins seem redundant:
discussions could be joined to subscriptions directly on the company_id column;
posts could be joined to subscriptions directly on the analyst_id column;
the last join to users in either SELECT is unnecessary as no data is retrieved from that table and the filter (users.id = 6) could be re-applied to subscriptions.user_id.
So, I would probably rewrite the query like this:
SELECT p.id
FROM posts p
INNER JOIN discussions d ON d.post_id = p.id
INNER JOIN subscription s
ON s.subscribable_type = 'Company' AND s.subscribable_id = d.company_id
OR s.subscribable_type = 'User' AND s.subscribable_id = p.analyst_id
WHERE s.user_id = 6

This is untested, but give this a try. Let me know if it works.
select posts.id
from posts
inner join discussions
on discussions.post_id = posts.id
inner join companies
on discussions.company_id = companies.id
inner join subscriptions
on subscriptions.subcribable_id = companies.id
inner join users
on subscriptions.user_id = users.id
or users.id = posts.analysis_id
where subscriptions.subscribable_type in ('Company', 'User')
and users.id = 6

I think there are differences I'm overlooking but if you just want the post if it's in the first or second without using a UNION then:
SELECT posts.id FROM posts
where posts.id IN
(
SELECT posts.id FROM posts
INNER JOIN discussions ON discussions.post_id = posts.id
INNER JOIN companies ON discussions.company_id = companies.id
INNER JOIN subscriptions ON subscriptions.subscribable_id = companies.id AND subscriptions.subscribable_type = 'Company'
INNER JOIN users ON subscriptions.user_id = users.id WHERE users.id = 6
)
or
posts.id IN
(
SELECT posts.id FROM posts
INNER JOIN users users_2 ON posts.analyst_id = users_2.id
INNER JOIN subscriptions ON subscriptions.subscribable_id = users_2.id AND subscriptions.subscribable_type = 'User'
INNER JOIN users ON subscriptions.user_id = users.id WHERE users.id = 6
)

Related

I need to group (tbl_types.name AS type) in a same row (postgres 14)

I need help with a select in postgres, I need to group X types into a single line, for example: type: multiple, trully, I need help on the type column
SELECT tbl_questions.id AS id,
tbl_questions.question AS question,
tbl_questions.year AS year,
tbl_question_responses.response_id AS response_id,
tbl_responses.response AS response_content,
tbl_responses.response_type AS response,
tbl_subjects.name AS subject,
tbl_categories.name AS category,
tbl_types.name AS type,
tbl_institutions.name AS institution
FROM tbl_questions
INNER JOIN tbl_question_responses ON tbl_questions.id = tbl_question_responses.question_id
INNER JOIN tbl_responses ON tbl_question_responses.response_id = tbl_responses.id
INNER JOIN tbl_question_subjects ON tbl_questions.id = tbl_question_subjects.question_id
INNER JOIN tbl_subjects ON tbl_subjects.id = tbl_question_subjects.subject_id
INNER JOIN tbl_question_categories ON tbl_questions.id = tbl_question_categories.question_id
INNER JOIN tbl_categories ON tbl_categories.id = tbl_question_categories.category_id
INNER JOIN tbl_question_types ON tbl_questions.id = tbl_question_types.question_id
INNER JOIN tbl_types ON tbl_types.id = tbl_question_types.type_id
INNER JOIN tbl_question_institutions ON tbl_question_institutions.question_id = tbl_questions.id
INNER JOIN tbl_institutions ON tbl_institutions.id = tbl_question_institutions.institution_id
WHERE tbl_questions.id = 'c7aa15cb-27e5-4f28-9141-483f7cce8e56'
This is a select result

SQL query optimization practice

I want to pratice my skills in SQL query optimization for small databases.
I have created a very simple database:
I populated it with hunderds of thousands of entries and need some complex, stupid and unoptimized queries to make some tests and practice my optimization skills. Right now I tried to write some queries but they don't make much impact on the database.
Which key words should I use? Could someone help?
I find it hard to come up with queries that are really bad :-) Here are some ideas. Hopefully, others will come up with more. I suggest you put your tables (empty or even with some sample rows) in https://dbfiddle.uk/, where we can try our queries. I don't know for instance, if I don't have any syntax errors in below queries.
Find all videos that are either owned or commented by a user named xyz:
select distinct v.*
from videos v
left join user_have_videos uv on uv.video_id = v.video_id
left join users u1 on u1.user_id = uv.user_id
left join comments c on c.video_id = v.video_id
left join users u2 on u2.user_id = c.user_id
where u1.user_name = :user_name
or u2.user_name = :user_name;
Find all users that own at least three different videos:
select distinct u.*
from users u
left join user_have_videos uv1 on uv1.user_id = u.user_id
left join videos v1 on v1.video_id = uv1.video_id
left join user_have_videos uv2 on uv2.user_id = u.user_id
left join videos v2 on v2.video_id = uv2.video_id
left join user_have_videos uv3 on uv3.user_id = u.user_id
left join videos v3 on v3.video_id = uv3.video_id
where v1.video_id <> v2.video_id
and v1.video_id <> v3.video_id
and v2.video_id <> v1.video_id
and v2.video_id <> v3.video_id
and v3.video_id <> v1.video_id
and v3.video_id <> v2.video_id;
Find all users that commented on John Wayne:
select distinct u.*
from users u
join comments c on c.user_id = u.user_id
where lower(comments) like '%john wayne%';
And here is one where you shall find out what this does and make it better:
with cte(user_id, video_id, num) as
(
select u.user_id, v.video_id, 1
from videos v
join user_have_videos uv on uv.video_id = v.video_id
join users u on u.user_id = uv.user_id
union all
select u.user_id, min(v.video_id), min(cte.num) + 1
from videos v
join user_have_videos uv on uv.video_id = v.video_id
join users u on u.user_id = uv.user_id
join cte on cte.user_id = u.user_id
and cte.video_id < v.video_id
group by u.user_id
)
select distinct u.*
from users u
join cte on cte.user_id = u.user_id and cte.num >= 10;

wordpress: query to select the last post from more than 1 specific category

I need to select the last post from 3 different categories.
Till now, I've got a query like this:
select ID, t.term_id term_id, post_title, post_content, t.slug as category
from e_posts p
inner join e_term_relationships tr on p.ID = tr.`object_id`
inner join e_term_taxonomy tt on tr.term_taxonomy_id = tt.term_taxonomy_id
inner join e_terms t on t.term_id = tt.term_id
where tt.taxonomy = "category" and t.term_id IN(4,670,158)
and the result is like this:
If I try to group by term_id, I get the first post from each term (category). Even if I order by date. What can I do? I guess it's not too hard but can't figure out..
You problem relates to greatest-n-per-group tag (take a look) you need to get the most recent posts where your criteria matches. So here is your solution: get the posts with their max date self join by using p.ID = tr.object_idAND p.post_date = tr.post_date so it takes care to get the recent posts
SELECT DISTINCT
p.ID,
t.term_id term_id,
p.post_title,
p.post_content,
t.slug AS category
FROM
e_posts p
INNER JOIN
(SELECT
etr.*,
pp.ID,
MAX(pp.post_date) post_date
FROM
e_posts pp
INNER JOIN
e_term_relationships etr
ON
pp.ID = etr .`object_id`
GROUP BY
pp.ID,etr.term_taxonomy_id) tr
ON
(p.ID = tr.`object_id` AND p.post_date = tr.post_date)
INNER JOIN
e_term_taxonomy tt
ON
tr.term_taxonomy_id = tt.term_taxonomy_id
INNER JOIN
e_terms t
ON
t.term_id = tt.term_id
WHERE
tt.taxonomy = "category" AND
t.term_id IN(4,670,158)

sql select query among 3 tables

I have three tables:
ITEMDISPLAYCOuNTS *this table stores who displayed which posts and howmanytimes
postid, count, whodisplayedid
POSTS *this table stores who posted what?
postid, whopostedid
ASPNET_USERS
userid, username
What I want at the end is who displayed whose post, and how many times, with usernames, not userids:
OUTPUT
UserNameWhoDisplayed, UserNameWhosePost, Count
I wrote the following code, but it is not functioning properly.
SELECT u1.UserName, u2.UserName, ItemDisplayCounts.Count
FROM ItemDisplayCounts AS i, Posts AS p, aspnet_Users AS u1, aspnet_Users AS u2
WHERE p.UserId = u2.UserId AND i.UserId = u1.UserId AND i.PostId = p.PostId
Can anyone suggest any corrections?
I think you want something like this (changed your joins to proper ANSI ones):
select
iu.UserName as UserNameWhoDisplayed,
pu.UserName as UserNameWhosePost,
sum(i.Count) as [Count]
from ItemDisplayCounts as i
inner join aspnet_Users as iu on iu.userid = i.whodisplayedid
inner join posts as p on p.postid = i.postid
inner join aspnet_Users as pu on pu.userid = p.whopostedid
group by
iu.UserName,
pu.UserName
If I am understanding you correctly you do not need to perform any math in the query, since you have the count field in the ITEMDISPLAYCOuNTS table. So, I do believe this will work for you:
SELECT U2.username AS UserNameWhoDisplayed,
U1.username AS UserNameWhosePost,
ID.[Count]
FROM ((POSTS AS P
INNER JOIN ASPNET_USERS AS U1 ON P.whopostedid = U1.userid)
INNER JOIN ITEMDISPLAYCOuNTS AS ID ON P.postid = ID.postid)
INNER JOIN ASPNET_USERS AS U2 ON ID.whodisplayedid = U2.userid

translating sql sub query to join

I had a long query, I short it out by using joins instead and resultant query is as below but still it has sub query. How to convert this sub query to join
SELECT
pav.post_id as Id, img.path as Path, attr.name as Name, pc.title as Category, pav.value_text as Valuess, post.created_on as createdOn
FROM
postings post inner join post_attributes_values pav on post.post_id = pav.post_id
left outer join images img on post.post_id = img.post_id and img.sequence='1'
inner join attributes attr on pav.attr_id = attr.attr_id
inner join categories_parent_categories pc on attr.cat_id = pc.category_id
where
pav.post_id in (select distinct post_id from post_attributes_values where value_text = 'SFX')
After reading your last comment to Matei's answer I have come to realize that you actually want ALL the posts where one of the attributes has value of 'SFX'. If I understood correctly, your only alternative is to add derived table and join by post_id:
SELECT pav.post_id AS Id,
img.path AS Path,
attr.name AS Name,
pc.title AS Category,
pav.value_text AS Valuess,
post.created_on AS createdOn
FROM postings post
INNER JOIN post_attributes_values pav
ON post.post_id = pav.post_id
LEFT OUTER JOIN images img
ON post.post_id = img.post_id
AND img.sequence = '1'
INNER JOIN attributes attr
ON pav.attr_id = attr.attr_id
INNER JOIN categories_parent_categories pc
ON attr.cat_id = pc.category_id
INNER JOIN
(
SELECT DISTINCT post_id
FROM post_attributes_values
WHERE value_text = 'SFX'
) sfxPosts
ON pav.post_id = sfxPosts.post_id
(Query reformatted thanks to instant sql formatter.)
Maybe this? Please test it
SELECT
pav.post_id as Id, img.path as Path, attr.name as Name, pc.title as Category, pav.value_text as Valuess, post.created_on as createdOn
FROM
postings post
inner join post_attributes_values pav on post.post_id = pav.post_id AND pav.value_text = 'SFX'
left outer join images img on post.post_id = img.post_id and img.sequence='1'
inner join attributes attr on pav.attr_id = attr.attr_id
inner join categories_parent_categories pc on attr.cat_id = pc.category_id