Joining Table that has no value - sql

Im working on a friendship site for a client that has been built by another developer. The client has added new questions to the questions table. I can't figure out a query that will return results for all the questions, even for old users who have not answered the new questions.
The issue is that old users dont have an entry in the user question table so how can I get a default of 'not answer' for old users who have not input a value in the user question table?
See table structure below
User Table
id | username
0 | louis
User Question Table
ID | USERID | Question ID | Answer ID
0 | 1 | 0 | 5
1 | 1 | 1 | 8
Question Table
ID | QUESTION
0 | What is your favorite color
1 | What is your gender
2 | What is your favorite t.v. show
Answer Table
ID | answer
5 | Blue
8 | female
This is my desired result:
user | question | answer
louis | What is your favorite color | blue
louis | What is your gender | female
louis | What is your height | Not Answered

I would get all questions, left join the answers and userquestions and do a cross join with users:
select
username,
question,
answer = isnull(answer,'Not Answered')
from Question q
cross join User u
left join UserQuestion uq on uq.QuestionID = q.ID and u.id = uq.USERID
left join Answer a on uq.AnswerID = a.ID
Sample SQL Fiddle

You want to use a cross join to get combinations of all users and all questions. Then use a left join to bring in the information about existing answers. The final piece is coalesce() to substitute a value when there is no answer:
select u.username, q.question, coalesce(a.answer, 'Not Answered')
from user u cross join
question q left join
userquestion uq
on uq.userid = u.id and
uq.questionid = q.id left join
answer a
on uq.answerid = a.id

Related

SQL Query to show results that don't have a relation to variable

For an assignment I have which includes a delete and add friend system (like Facebook), I've made a query that works by using two SQL tables, one which includes a friend_id, name and other information, and another which holds two friend_id columns, that show the relationship with the users and if they're friends.
User Table (friends)
| friend_id | profile_name |
|:---------- |:------------:|
| 1 | John |
| 2 | Peter |
| 3 | Alex |
| 4 | Nick |
---------------------------
Friendship Table (myfriends)
| friend_id1 | friend_id2 |
|:---------- |:----------:|
| 1 | 3 |
| 2 | 4 |
| 3 | 1 |
| 4 | 2 |
-------------------------
I am wanting to get a query which selects people that don't have a connection with a result (I want to show anyone who doesn't have a connection to friend_id '1', so only want to show users 2 and 4), and then display their name.
I have a query that selects the ones which have the relation which is:
SELECT friends.profile_name,friends.friend_id FROM `myfriends` JOIN `friends` ON friends.friend_id = myfriends.friend_id2 WHERE `friend_id1` = 1;
The query bellow shows all results from the table, and even using '!=', it doesn't select those who don't have a relation to friend_id '1'
SELECT friends.profile_name,friends.friend_id FROM `myfriends` JOIN `friends` ON friends.friend_id = myfriends.friend_id2 WHERE `friend_id1` != 1;
How can I fix this query so it shows all results but those connected to ‘friend_id1’ = 1
with connected as (SELECT friend_id,
myfriends.friend_id2 friend
FROM myfriends
JOIN friends
ON friends.friend_id = myfriends.friend_id1
WHERE friend_id1 = 1)
select *
from friends
where friend_id not in (select distinct friend from connected union all select distinct friend_id from connected)
you cannot change the where clause as it specifies which user you want to focus on.
So first get the users that are connected (in the first cte), and then select all users except those found in the first result of the connected users.
By the way, your example is misleading as it can be solved with a bug by doing something simple in the join.
edit
while it wasn't clease which version you were using, (I thought with clause is available in the newer mysql versions) I created another solution that is working on mysql 5.6 and should work for you as well:
select f.*
from friends f
left join (
SELECT friend_id, myfriends.friend_id2 friend
FROM myfriends
JOIN friends
ON friends.friend_id = myfriends.friend_id1
WHERE 1 in (friend_id,friend_id2)) f1
on f1.friend = f.friend_id
where f1.friend is null
it has a nicer implementation in one part (1 in one of 2 columns), and uses a left join that takes the nulls from the right table.

Complex SQL Joins with Where Clauses

Being pretty new to SQL, I ask for your patience. I have been banging my head trying to figure out how to create this VIEW by joining 3 tables. I am going to use mock tables, etc to keep this very simple. So that I can try to understand the answer - no just copy and paste.
ICS_Supplies:
Supplies_ ID Item_Description
-------------------------------------
1 | PaperClips
2 | Rubber Bands
3 | Stamps
4 | Staples
ICS_Orders:
ID SuppliesID RequisitionNumber
----------------------------------------------------
1 | 1 | R1234a
6 | 4 | R1234a
2 | 1 | P2345b
3 | 2 | P3456c
4 | 3 | R4567d
5 | 4 | P5678e
ICS_Transactions:
ID RequsitionNumber OrigDate TransType OpenClosed
------------------------------------------------------------------
1 | R1234a | 06/12/20 | Req | Open
2 | P2345b | 07/09/20 | PO | Open
3 | P3456c | 07/14/20 | PO | Closed
4 | R4567d | 08/22/20 | Req | Open
5 | P5678e | 11/11/20 | PO | Open
And this is what I want to see in my View Results
Supplies_ID Item RequsitionNumber OriginalDate TransType OpenClosed
---------------------------------------------------------------------------------------
1 | Paper Clips | P2345b | 07/09/20 | PO | OPEN
2 | Rubber Bands | Null | Null | Null | Null
3 | Stamps | Null | Null | Null | Null
4 | Staples | P56783 | 11/11/20 | PO | OPEN
I just can't get there. I want to always have the same amount of records that we have in the ICS_Supplies Table. I need to join to the ICS_Orders Table in order to grab the Requisition Number because that's what I need to join on the ICS_Transactions Table. I don't want to see data in the new added fields UNLESS ICS_Transactions.TransType = 'PO' AND ICS_Transactions.OpenClosed = 'OPEN', otherwise the joined fields should be seen as null, regardless to what they contain. IF that is possible?
My research shows this is probably a LEFT Join, which is very new to me. I had made many attempts on my own, and then posted my question yesterday. But I was struggling to ask the correct question and it was recommended by other members that I post the question again . .
If needed, I can share what I have done, but I fear it will make things overly confusing as I was going in the wrong direction.
I am adding a link to the original question, for those that need some background info
Original Question
If there is any additional information needed, just ask. I do apologize in advance if I have left out any needed details.
This is a bit tricky, because you want to exclude rows in the second table depending on whether there is a match in the third table - so two left joins are not what you are after.
I think this implements the logic you want:
select s.supplies_id, s.item_description,
t.requisition_number, t.original_date, t.trans_type, t.open_closed
from ics_supplies s
left join ics_transaction t
on t.transtype = 'PO'
and t.open_closed = 'Open'
and exists (
select 1
from ics_order o
where o.supplies_id = s.supplies_id and o.requisition_number = t.requisition_number
)
Another way to phrase this would be an inner join in a subquery, then a left join:
select s.supplies_id, s.item_description,
t.requisition_number, t.original_date, t.trans_type, t.open_closed
from ics_supplies s
left join (
select o.supplies_id, t.*
from ics_order o
inner join ics_transaction t
on t.requisition_number = o.requisition_number
where t.transtype = 'PO' and t.open_closed = 'Open'
) t on t.supplies_id = s.supplies_id
This query should return the data for supplies. The left join will add in all orders that have a supply_id (and return null for the orders that don't).
select
s.supplies_id
,s.Item_Description as [Item]
,t.RequisitionNumber
,t.OrigDate as [OriginalDate]
,t.TransType
,t.OpenClosed
from ICS_Supplies s
left join ICS_Orders o on o.supplies_id = s.supplies_id
left join ICS_Transactions t on t.RequisitionNumber = o.RequisitionNumber
where t.TransType = 'PO'
and t.OpenClosed = 'Open'
The null values will automatically show null if the record doesn't exist. For example, you are joining to the Transactions table and if there isn't a transaction_id for that supply then it will return 'null'.
Modify your query, run it, then maybe update your question using real examples if it's possible.
In the original question you wrote:
"I only need ONE matching record from the ICS_Transactions Table.
Ideally, the one that I want is the most current
'ICS_Transactions.OriginalDate'."
So the goal is to get the most recent transaction for which the TransType is 'PO' and OpenClosed is 'Open'. That the purpose of the CTE 'oa_cte' in this code. The appropriate transactions are then LEFT JOIN'ed on SuppliesId. Something like this
with oa_cte(SuppliesId, RequsitionNumber, OriginalDate,
TransType, OpenClosed, RowNum) as (
select o.SuppliesId, o.RequsitionNumber,
t.OrigDate, t.TransType, t.OpenClosed,
row_number() over (partition by o.SuppliesId
order by t.OrigDate desc)
from ICS_Orders o
join ICS_Transactions t on o.RequisitionNumber=t.RequisitionNumber
where t.TransType='PO'
and t.OpenClosed='OPEN')
select s.*, oa.*
from ICS_Supplies s
left join oa_cte oa on s.SuppliesId=oa.SuppliesId
and oa.RowNum=1;

A double outer join

I have a table People, a table Permission and a table Action. Permission is there to provide a many-to-many relationship between People and Action.
My goal is simple : I want to look for one person in People, I want to see the permission listed in Permission, AND every action available, wether the person has the permission or not.
Note that a member of People is not necessary represented in Permission. So, "John" is only allowed to do something if he's got the permission written in the database.
Ideally, the result would look something like :
+-------------------------------------------+
+ People.name | Action.name |Allowed |
+-------------------------------------------+
| John Doe | Launching missiles | N |
| John Doe | Deleting code | Y |
+-------------------------------------------+
Before I get to there, I'm simply tring to build a simple query with every data.
Getting the list of permission of someone, even if he is not present in the permission table is easy :
SELECT * FROM people pp LEFT OUTER JOIN permission pr ON pp.id = pr.people_id
WHERE pp.name = 'John';
Getting the list of permission and actions is easy :
SELECT * FROM permission pr LEFT OUTER JOIN action a ON pr.action_id = a.id
However, I can't seem to perform a double outer join that would get me everything I want :
SELECT * FROM people pp LEFT OUTER JOIN permission pr ON pp.id = pr.permission_id
LEFT OUTER JOIN people WHERE pp.name = 'John'
Shows John, the fact that he's got no permission... but no actions whatsoever.
In other words, results look something like :
+-----------------------------------------+
+ Name | People_id | Action_id |Action |
+-----------------------------------------+
| John | | | |
+-----------------------------------------+
When I wanted :
+-----------------------------------------------------------+
+ Name | People_id | Action_id |Action |
+-----------------------------------------------------------+
| John | | | Launching missiles |
| John | | | Deleting codes |
| John | | | (every other actions) |
+-----------------------------------------------------------+
So, is what I want possible ? If so, how can I achieve it ? (I'm using PostgreSQL).
edit: Here is a sqlfiddle : http://sqlfiddle.com/#!2/6d502
SELECT pp.name, pr.id_people, pr.id_action, a.name as action_name
FROM people pp
CROSS JOIN action a
LEFT JOIN permission pr
ON pr.id_people = pp.id
AND pr.id_action = a.id
SQLFiddle: http://sqlfiddle.com/#!15/5a7b1/4
To filter by person name add to query:
WHERE pp.name = 'John'
This should work.
SELECT
P.name as "people.name",
IFNULL(pm.id_people,''),
IFNULL(pm.id_action,''),
A.name as "action.name"
FROM Action A
CROSS JOIN People P
LEFT JOIN permission pm
ON pm.id_action = a.id
AND pm.id_people = p.id
WHERE p.name = 'John'

SQL: find the earliest occurrence of a row in a table based of values from multiple tables

I realize this question has been asked many times and I am have looked through many posts concerning this issue for over a day now. Unfortunately I've been unable to resolve my specific issue and would greatly appreciate any help that you all can offer.
I believe this is a variation of the "greatest-n-per-group" problem that comes up on StackOverflow several times per week.
The query is being run against a database for work tickets. The data I need is spread out across many table and I am consolidating it into a single area to work with. In this specific instance, the update table contains multiple rows for a specific work ticket. I am trying to join it to the ticket table and only grab the earliest update-date for that ticket where the user id matches the user associated with the ticket from the user table. Basically I'm using when the user_id is changed to determine when a ticket is being assigned to a particular user.
UPDATE_TABLE
updateNumber | user_id | date | ticket_number
11 | 4586b03 | 2011-11-30 | 923479283
12 | 6786t03 | 2011-11-30 | 923479283
13 | 7986003 | 2011-12-02 | 923479283
14 | 7986003 | 2011-12-03 | 923479283
15 | 7986003 | 2011-12-04 | 923479283
16 | 5838397 | 2011-10-02 | 391983247
17 | 7986004 | 2012-01-03 | 663738223
18 | 7986003 | 2011-08-04 | 391983247
Query:
select
min(TA.updated_at) as UpdateVal
from Tickets T
inner join Users U on U.id = T.assigned_user_id
inner join UserGroup AU on U.login = AU.[User]
inner join TicketUpdate TA on TA.task_id = T.id
where
TA.task_id = 923479283 and TA.user_id = 7986003
returns: 2011-12-02
This approach works when I hard code the task_id and user_id for a specific row like above. However when I substitute the T.id for 923479283 and U.id for 7986003 to get this info for all the tickets the query returns nothing. I've tried many different solutions including joining the TicketUpdate table on itself via alias and so on.
Please HELP!
You need to add those columns on your SELECT and GROUP BY:
select TA.task_id, TA.user_id, min(TA.updated_at) as UpdateVal
from Tickets T
inner join Users U on U.id = T.assigned_user_id
inner join UserGroup AU on U.login = AU.[User]
inner join TicketUpdate TA on TA.task_id = T.id
GROUP BY TA.task_id, TA.user_id

How should joins used in mysql?

If i have two tables like
user table-"u"
userid | name
1 | lenova
2 | acer
3 | hp
pass table-"p"
userid | password
1 | len123
2 | acer123
3 | hp123
as for as i learnt from tutorials I can join these 2 tables using many joins available in
mysql as said here
If i have a table like
role table-"r"
roleid | rname
1 | admin
2 | user
3 | dataanalyst
token table-"t"
tokenid| tname
1 | xxxx
2 | yyyy
3 | zzzz
tole_token_association table-"a"
roleid | tokenid
1 | 1
1 | 2
3 | 1
3 | 3
3 | 1
I have to make a join such that I have to display a table which corresponds
like this "rolename" has all these tokens.How to make this? I am confused. Is it possible to make a join? I am liking mysql a lot. I wish to play with queries such that not playing. I want to get well versed. Any Suggestions Please?
It's easiest to see when the column names that need to be joined are named identically:
SELECT r.rname,
t.tname
FROM ROLE r
JOIN ROLE_TOKEN_ASSOCIATION rta ON rta.roleid = r.roleid
JOIN TOKEN t ON t.tokenid = rta.tokenid
This will return only the roles with tokens associated. If you have a role that doesn't have a token associated, you need to use an OUTER join, like this:
SELECT r.rname,
t.tname
FROM ROLE r
LEFT JOIN ROLE_TOKEN_ASSOCIATION rta ON rta.roleid = r.roleid
JOIN TOKEN t ON t.tokenid = rta.tokenid
This link might help -- it's a visual representation of JOINs.