Get Data from two different tables on a condition - sql

I got situation like below:
Posts and like tables are respectively:
----------------- --------------------------
| post_id | text | | post_id | like | person|
------------------- ---------------------------
| 1 | hello | | 1 | yes | Jhon |
| 2 | Haii | | 1 | yes | Sham |
| 3 | I am..| | 1 | yes | Ram |
-------------------- | 2 | yes | Mahe |
----------------------------
Now I want to get all posts and I want to know whether each post is liked by Sham or not.
So result will be:
-----------------------------------
| post_id | text | liked_by_Sham |
-----------------------------------
| 1 | hello | yes |
| 2 | Haii | no |
| 3 | I am | no |
------------------------------------
As I am new to SQL can anyone explain me how to do that. I tried it using Inner join but it doesn't work.
I tried with below query:
select posts.*,liketb.like
from posts
inner join liketb
on posts.post_id = liketb.post_id
where liketb.person = 'Sham';
This query is giving only posts liked by sham.

Use left join, with case. like is a keyword. Make sure it is properly escaped.
select p.post_id, p.text,
case when l.like = 'yes' then l.like else 'no' end as liked_by_sham
from posts p
left join liketb l on p.post_id = l.post_id and l.person = 'Sham';

Sql Fiddle Demo
You need put the filter clausule on the ON section to get all post with yes, no or null when no match. And use nvl to convert all NULL to 'no'
SELECT p."post_id", p."text", nvl(l."like", 'no') as liked_by_sham
FROM posts p
LEFT JOIN liketb l
ON p."post_id" = l."post_id"
AND l."person" = 'Sham'
OUTPUT
| post_id | text | LIKED_BY_SHAM |
|---------|--------|---------------|
| 1 | hello | yes |
| 3 | I am.. | no |
| 2 | Haii | no |

Related

PostgreSQL check if value exists in another table

I'm trying to find a solution in PostgreSQL of how I can add to the output of the query extra column with value if id exists in another table or not:
I need several things:
Do a join between two tables
Add a new column into the result output where I check if exists in the third table or not
My tables:
announcement
author
chapter
announcement table
| id | author_id | date | group_id | ... |
author table
| id | name | email | ... |
chapter table
| id | announcement_id | ... |
This is what I have now. I did a left outer join and it works as I expected:
select announcement.id, announcement.date, author.id as publisher_id, author.name as publisher_name
from announcement
left outer join author
on announcement.author_id = author.id
where announcement.group_id = 123 and announcement.date >= '2022-06-01'::date;
with output:
| id | date | publisher_id | publisher_name |
| 1 | 2020-07-01 | 12 | John |
| 2 | 2020-07-04 | 123 | Arthur |
Now I can't find a solution of how to add an extra column with_chapters to the query response, where I will check if announcement.id exists in chapter table under announcement_id column.
For example, chapter table can have such data:
| id | announcement_id |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
So we see that some announcements can appear in chapters several times (so i'm looking for at least 1 match). And some announcements doesn't have chapters at all.
Output finally should be like that:
| id | date | publisher_id | publisher_name | with_chapters |
| 1 | 2020-07-01 | 12 | John | true |
| 2 | 2020-07-04 | 123 | Arthur | false |
Thanks a lot for any help :)
While EXISTS (subquery) is usually used in the WHERE clause, it returns an ordinary Boolean and so can be used in the select list.
SELECT blah1, blah2,
EXISTS (select 1 from chapter where chapter.announcement_id=announcement.id) as with_chapter
FROM ...

Select from a concatenation of two columns after a left join

Problem description
Let the tables C and V have those values
>> Table V <<
| UnID | BillID | ProductDesc | Value | ... |
| 1 | 1 | 'Orange Juice' | 3.05 | ... |
| 1 | 1 | 'Apple Juice' | 3.05 | ... |
| 1 | 2 | 'Pizza' | 12.05 | ... |
| 1 | 2 | 'Chocolates' | 9.98 | ... |
| 1 | 2 | 'Honey' | 15.98 | ... |
| 1 | 3 | 'Bread' | 3.98 | ... |
| 2 | 1 | 'Yogurt' | 8.55 | ... |
| 2 | 1 | 'Ice Cream' | 7.05 | ... |
| 2 | 1 | 'Beer' | 9.98 | ... |
| 2 | 2 | 'League of Legends RP' | 40.00 | ... |
>> Table C <<
| UnID | BillID | ClientName | ... |
| 1 | 1 | 'Alexander' | ... |
| 1 | 2 | 'Tom' | ... |
| 1 | 3 | 'Julia' | ... |
| 2 | 1 | 'Tom' | ... |
| 2 | 2 | 'Alexander' | ... |
Table C have the values of each product, which is associated with a bill number. Table V has the relationship between the client name and the bill number. However, the bill number has a counter that is dependent on the UnId, which is the store unity ID. That being said, each store has it`s own Bill number 1, number 2, etc. Also, the number of bills from each store are not equal.
Solution description
I'm trying to make select between the C left join V without sucess. Because each BillID is dependent on the UnID, I have to make the join considering the concatenation between those two columns.
I've used this script, but it gives me an error.
SELECT
SUM(C.Value),
V.ClientName
FROM
C
LEFT JOIN
V
ON
CONCAT(C.UnID, C.BillID) = CONCAT(V.UnID, V.BillID)
GROUP BY
V.ClientName
and SQL server returns me this 'CONCAT' is not a recognized built-in function name.
I'm using Microsoft SQL Server 2008 R2
Is the use of CONCAT wrong? Or is it the way I tried to SELECT? Could you give me a hand?
[OBS: The tables I've present you are just for the purpose of explaining my difficulties. That being said, if you find any errors in the explanation, please let me know to correct them.]
You should be joining on the equality of the UnID and BillID columns in the two tables:
SELECT
c.ClientName,
COALESCE(SUM(v.Value), 0) AS total
FROM C c
LEFT JOIN V v
ON c.UnID = v.UnID AND
c.BillID = v.BillID
GROUP BY
c.ClientName;
In theory you could try joining on CONCAT(UnID, BillID). However, you could run into problems. For example, UnID = 1 with BillID = 23 would, concatenated together, be the same as UnID = 12 and BillID = 3.
Note: We wrap the sum with COALESCE, because should a given client have no entries in the V table, the sum would return NULL, which we then replace with zero.
concat is only available in sql server 2012.
Here's one option.
SELECT
SUM(C.Value),
V.ClientName
FROM
C
LEFT JOIN
V
ON
cast(C.UnID as varchar(100)) + cast(C.BillID as varchar(100)) = cast(V.UnID as varchar(100)) + cast(V.BillID as varchar(100))
GROUP BY
V.ClientName

What Clause would most optimally create this query?

So I don't have much experience with SQL, and am trying to learn. An interview question I came across had this question. I'm trying to learn more SQL but maybe I'm missing a piece of info to solve this? Or maybe I'm approaching the problem wrong.
This is the question:
We have following two tables , below is their info:
POLICY (id as int, policy_content as varchar2)
POLICY_VOTES (vote as boolean, policy_id as int)
Write a single query that returns the policy_id, number of yes(true) votes and number of no(false) votes with a row for each policy up for a vote stored
My first thought when approaching this was to use a WITH clause to get the policy_ids and use an inner join to get the votes for yes and no but I can't find a way to make it work, which is what leads me to believe that there's another clause in SQL I'm not aware of or couldn't find that would make it easier. Either that or I'm thinking of the problem in the wrong way.
Good question.
I cannot answer too specifically, since you did not specify a DBMS, but what you will want to do is count or situationally sum based on criteria. When you use an aggregate function like that, you also need GROUP BY.
Here are two example tables I made with test data:
policy
| id | policy_content |
|----|----------------|
| 1 | foo |
| 2 | foo |
| 3 | foo |
| 4 | foo |
| 5 | foo |
policy votes
| vote | policy_id |
|------|-----------|
| yes | 1 |
| no | 1 |
| yes | 2 |
| yes | 2 |
| no | 3 |
| no | 3 |
| no | 4 |
| yes | 4 |
| yes | 5 |
| yes | 5 |
Using the below query:
SELECT
policy_votes.policy_id,
SUM(CASE WHEN vote = 'yes' THEN 1 ELSE 0 END) AS yes_votes,
SUM(CASE WHEN vote = 'no' THEN 1 ELSE 0 END) AS no_votes
FROM
policy_votes
GROUP BY
policy_votes.policy_id
You get:
| POLICY_ID | YES_VOTES | NO_VOTES |
|-----------|-----------|----------|
| 1 | 1 | 1 |
| 2 | 2 | 0 |
| 4 | 1 | 1 |
| 5 | 2 | 0 |
| 3 | 0 | 2 |
Here is an SQL Fiddle for you to try it out.
Try this:
select p.id, p.content,
Count(case when pv.vote='true' then 1 end) as number_of_yes,
Count(case when pv.vote='false' then 1 end) as number_of_no
From policy p join policy_votes pv
On(p.id = pv.policy_id)
Group by p.id, p.content
Cheers!!

Select all rows where rows in another joined table match condition

So I want to select all rows where a subset of rows in another table match the given values.
I have following tables:
Main Profile:
+----+--------+---------------+---------+
| id | name | subprofile_id | version |
+----+--------+---------------+---------+
| 1 | Main 1 | 4 | 1 |
| 2 | Main 1 | 5 | 2 |
| 3 | Main 2 | ... | 1 |
+----+--------+---------------+---------+
Sub Profile:
+---------------+----------+
| subprofile_id | block_id |
+---------------+----------+
| 4 | 6 |
| 4 | 7 |
| 5 | 8 |
| 5 | 9 |
+---------------+----------+
Block:
+----------+-------------+
| block_id | property_id |
+----------+-------------+
| 7 | 10 |
| 7 | 11 |
| 7 | 12 |
| 7 | 13 |
| 8 | 14 |
| 8 | 15 |
| 8 | 16 |
| 8 | 17 |
| ... | ... |
+----------+-------------+
Property:
+----+--------------------+--------------------------+
| id | name | value |
+----+--------------------+--------------------------+
| 10 | Description | XY |
| 11 | Responsible person | Mr. Smith |
| 12 | ... | ... |
| 13 | ... | ... |
| 14 | Description | XY |
| 15 | Responsible person | Mrs. Brown |
| 16 | ... | ... |
| 17 | ... | ... |
+----+--------------------+--------------------------+
The user can define multiple conditions on the property table. For example:
Description = 'XY'
Responsible person = 'Mr. Smith'
I need all 'Main Profiles' with the highest version which have ALL matching properties and can have more of course which do not match.
It should be doable in JPA because i would translate it into QueryDSL to build typesafe, dynamic queries with the users input.
I already searched trough all questions regarding similar problems but couldn't project the answer onto my problem.
Also, I've already tried to write a query which worked quite good but retrieved all rows with at least one matching condition. Therefore i need all properties in my set but it only fetched (fetch join, which is missing in my code examplte) the matching ones.
from MainProfile as mainProfile
left join mainProfile.subProfile as subProfile
left join subProfile.blocks as block
left join block.properties as property
where mainProfile.version = (select max(mainProfile2.version)from MainProfile as mainProfile2 where mainProfile2.name = mainProfile.name) and ((property.name = 'Description' and property.value = 'XY') or (property.name = 'Responsible person' and property.value = 'Mr. Smith'))
Running my query i got two rows:
Main 1 with version 2
Main 2 with version 1
I would have expected to get only one row due to mismatch of 'responsible person' in 'Main 2'
EDIT 1:
So I found a solution which works but could be improved:
select distinct mainProfile
from MainProfile as mainProfile
left join mainProfile.subProfile as subProfile
left join subProfile.blocks as block
left join block.properties as property
where mainProfile.version = (select max(mainProfile2.version)from MainProfile mainProfile2 where mainProfile2.name = mainProfile.name)
and ((property.name = 'Description' and property.content = 'XY') or (property.name = 'Responsible person' and property.content = 'Mr. Smith'))
group by mainProfile.id
having count (distinct property) = 2
It actually retrieves the right 'Main Profiles'. But the problem is, that only the two found properties are getting fetched. I need all properties though because of further processing.

MS Access SQL - Aggregate Count with 2 Example Table

I have a situation where I need to query from a 2 table in MS Access.
I'm new to this SQL access.
Table: UserInfo
===================
| UserID | Gender |
===================
| K01 | M |
| K02 | M |
| K03 | F |
| K04 | M |
===================
Table: OrderInfo
===================================
| OrderID | Type | UserID_FK |
===================================
| 1 | Food | K01 |
| 2 | Food | K01 |
| 3 | Toolkit | K02 |
| 4 | Food | K04 |
| 5 | Toolkit | K03 |
===================================
The question is:
I want to query so the result produce this table, how can I do it in MS Access?
I'm thinking that I should do a subquery but I don't know how to do it.
Table: Summary
================================================================
| UserID | Gender | CountOfToolkit | CountOfFood | TotalCount |
================================================================
| K01 | M | 0 | 2 | 2 |
| K02 | M | 1 | 0 | 1 |
| K03 | F | 1 | 0 | 1 |
| K04 | M | 0 | 1 | 1 |
================================================================
First, Type is a reserved word in MS Access.
You should avoid using reserved words.
Having said that, try this:
SELECT a.UserID, a.Gender, SUM(IIF(b.[Type] ='Food',1,0)) AS CountOfFood, SUM(IIF(b.[Type] ='Toolkit',1,0)) AS CountOfToolkit, COUNT(*) AS TotalCount
FROM UserInfo a INNER JOIN OrderInfo b ON a.UserId = b.UserID_FK
GROUP BY a.UserID, a.Gender
This crosstab query doesn't rely on having just Food & Toolkit as your types - it will count any new types you add.
The NZ wrapped around the Count(sType) ensures the 0 values are shown, while the CLNG ensures it's still treated as a number rather than text.
You could just use Count(sType) AS CountOfType.
TRANSFORM CLNG(NZ(Count(sType),0)) AS CountOfType
SELECT UserID
,Gender
,COUNT(sType) AS TotalCount
FROM UserInfo LEFT JOIN OrderInfo ON UserInfo.UserID = OrderInfo.UserID_FK
GROUP BY UserID, Gender
PIVOT sType