SELECT certain fields based on certain WHERE conditions - sql

I am writing an advanced MySQL query that searches a database and retrieves user information. What I am wondering is can I select certain fields if WHERE condition 1 is met and select other fields if WHERE condition 2 is met?
Database: users
------------------------
| user_id | first_name |
------------------------
| 1 | John |
------------------------
| 2 | Chris |
------------------------
| 3 | Sam |
------------------------
| 4 | Megan |
------------------------
Database: friendship
--------------------------------------
| user_id_one | user_id_two | status |
--------------------------------------
| 2 | 4 | 0 |
--------------------------------------
| 4 | 1 | 1 |
--------------------------------------
Status 0 = Unconfirmed
Status 1 = Confirmed
OK, as you can see John & Megan are confirmed friends while Chris & Megan are friends but the relationship is unconfirmed.
The query I am trying to write is as follow: Megan(4) searches for new friends I want all of the users except for the ones she is a confirmed friend with to be returned. So, the results should return 2,3. But since a relationship with user_id 2 exists but is not confirmed, I want to also return the status since an entry in the friendship table does exist between the two. If a user exist but there is no connection in the relationship table it still returns that users information but returns status as a NULL or doesn't return status at all since it doesn't exist in that table.
I hope this makes since. Ask questions if you need to.

Why not use a left join or an if-not-exists?
SELECT users.*
FROM (users LEFT JOIN friendships
ON status=1 AND (user_id_one=user_id OR user_id_two=user_id) )
WHERE
status IS NULL
or
SELECT users.*
FROM users
WHERE
NOT EXISTS (SELECT *
FROM friendships
WHERE status=1
AND (user_id_one=user_id
OR user_id_two=user_id))

You can create to separate queries and then UNION the result tables. In each query, add a field that always has the same value.
So something like this should work:
(SELECT id, 'Not Friends' As Status FROM t1 WHERE condition1)
UNION
(SELECT id, 'Unconfirmed' As Status FROM t1 WHERE condition2)
Just make sure the same number and name of fields exists in both queries.

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.

SQL Server query - don't want multiple rows with identical data

I have a SQL Server database that has the following three tables - this is simplified for this post.
Stakeholder table (a table that stores a persons personal data... name, address city, state, zip, etc)
Stakeholder_id full_name
---------------------------------------
1 Joe Stakeholder
2 Eric Stakeholder
SH Inquiry table (a table that stores information about when a stakeholder contacts us)
sh_inquiry_id inquiry_link_ID
-----------------------------------------------
1 1
2 1
3 2
Sh Contacts (a table that stores information about when we contact a stakeholder)
sh_contact_id contact_link_id
-----------------------------------------
1 1
2 1
3 2
I want to write a SQL query that shows the stakeholder information once then show all inquiries and all contacts underneath the stakeholder row? is that possible with SQL? So in this case joe stakeholder would be shown once and then there would be 4 rows next (2 inquiries and 2 contacts). Eric stakeholder would be shown once with two rows, 1 inquiry and one contact.
Thanks for any assistance in advance.
As has already been mentioned, you probably want to handle this in your application code. However, you can use a UNION query to sort of do what you want.
With the query below, I changed your latter 2 tables to SH_Inquiry and SH_Contacts (replaced spaces with underscores), which is generally a good habit (it's a bad idea to have spaces in your object names). Also, depending on how your tables are laid out, you might want to merge your Contacts and Inquiry tables (e.g. have one table, with a contact_type field that identifies it as "inbound" or "outbound").
Anyways, using a CTE and union:
WITH Unionized AS
(
SELECT
stakeholder_id,
full_name,
NULL AS contact_or_inquiry,
NULL AS contact_or_inquiry_id
FROM Stakeholder
UNION ALL
SELECT
inquiry_link_id AS stakeholder_id,
NULL AS full_name,
'inquiry' AS contact_or_inquiry,
sh_inquiry_id AS contact_or_inquiry_id
FROM SH_Inquiry
UNION ALL
SELECT
contact_link_id AS stakeholder_id,
NULL AS full_name,
'contact' AS contact,
sh_contact_id AS contact_or_inquiry_id
FROM SH_Contacts
)
SELECT
full_name,
contact_or_inquiry,
contact_or_inquiry_id
FROM Unionized
ORDER BY
stakeholder_id,
contact_or_inquiry,
contact_or_inquiry_id
giving you these results:
+------------------+--------------------+-----------------------+
| full_name | contact_or_inquiry | contact_or_inquiry_id |
+------------------+--------------------+-----------------------+
| Joe Stakeholder | NULL | NULL |
| NULL | contact | 1 |
| NULL | contact | 2 |
| NULL | inquiry | 2 |
| Eric Stakeholder | NULL | NULL |
| NULL | contact | 3 |
| NULL | inquiry | 1 |
| NULL | inquiry | 3 |
+------------------+--------------------+-----------------------+

Postgres Many to many mapping sql query

Postgresql Database
Table User
-----------
ID | Name
1 | John
2 | Bob
3 | Sarah
Table Photo
-------------
ID | Caption
1 | Vacation
2 | Birthday
3 | Christmas
Table Comment
--------------
ID | User ID | Photo ID| Text
1 | 1 | 1 | Mexico Looks Great
2 | 2 | 1 | Sure Does
3 | 3 | 1 | Too Hot
4 | 1 | 2 | Look at that cake
5 | 3 | 2 | No ice cream?
6 | 1 | 3 | So Happy
Desire: I want to get all the photos that ONLY John(1) and Sara(3) commented on.
How do I build a SQL query that looks for photos that only have comments from user #1 and user #3, I want to EXCLUDE results where more(or less) than those two commented on.
The clearest and most readable way, is the Photos containing comments by:
User1 Intersect User2 Except Any other user
This SQL Fiddle and query will return that:
SELECT *
FROM Photo
WHERE ID IN (
SELECT "Photo ID" FROM Comment WHERE "User ID" = 1
INTERSECT
SELECT "Photo ID" FROM Comment WHERE "User ID" = 3
EXCEPT
SELECT "Photo ID" FROM Comment WHERE "User ID" NOT IN (1, 3)
)
lets do three joins, one for john, one for sara, one for everyone else. Then we'll limit what we get back with the where clause.
select p.*
from photo p
left join comment john on john.photo_id=p.photo_id and john.user_id=1
left join comment sara on sara.photo_id=p.photo_id and sara.user_id=3
left join comment everyone_else on everyone_else.photo_id=p.photo_id and everyone_else.user_id<>3 and everyone_else.user_id<>1
where
everyone_else.id is null
and john.id is not null
and sara.id is not null
There are a couple of ways to do this. One is to use count with case:
select photoid
from comment
group by photoid
having count(distinct userid) = 2
and count(case when userid not in (1,3) then 1 end) = 0
SQL Fiddle Demo
Basically, make sure 2 users have commented and then make sure only user 1 or 3 commented.
You could use an intersection to find only the common photos, which would exclude photos commented by John but not Sarah, or vice versa
select photo_id from comment where user_id = 1
intersect
select photo_id from comment where user_id = 3

SQL Getting last date and values associated with description of value in another table

I have two different tables, my first table contains the authorizations granted to other requests. It has the following columns:
Authorizations table
| authorization_date | role_id | request_id |
|--------------------|---------|------------|
| 2011-08-02 | 1 | 168 |
| 2011-08-10 | 2 | 168 |
| 2011-08-20 | 6 | 168 |
| 2011-08-03 | 2 | 169 |
| 2011-08-24 | 6 | 169 |
| 2011-08-05 | 3 | 170 |
| 2011-08-09 | 5 | 170 |
As you can see, different people have different roles and also can grant a certain level of authorization. The higher the role, the higher the request has been processed.
Now, what I want to do is I want to show the description associated to the role_id (which is in another table) and ONLY the last authorization. Since I have the date for it I already know which one is the latest one. However I don't know how to do this as I try to group my query by getting the maximum value of the date, when I link it to my second table containing only the description of the role_id, I get duplicates and I just can't think of a way to do this, I'm kind of new with queries & as I've been learning by myself I don't know many things. Any ideas?
Thanks!
I am guessing a little here, but I think you want:
SELECT Authorizations.*, anotherTable.description
FROM
(
SELECT MAX(authorization_date) AS max_date, request_id
FROM Authorizations
GROUP BY request_id
) last_auths
INNER JOIN Authorizations ON last_auths.max_date = Authorizations.authorization_date
AND last_auths.request_id = Authorizations.request_id
INNER JOIN anotherTable ON anotherTable.role_id = Authorizations.role_id
The derived table will get the maximum authorization date for each request. Then you can join to get the role_id for that request and join again to get the description.
SELECT d.role_desc, a.role_id, MAX(a.authorization_date)
FROM Authorizations a
INNER JOIN Description d ON a.role_id = d.role_id
GROUP BY a.role_id, d.role_desc, d.role_id
Try this query and ensure you are getting the expected result.

How to join mysql tables

I've an old table like this:
user> id | name | address | comments
And now I've to create an "alias" table to allow some users to have an alias name for some reasons. I've created a new table 'user_alias' like this:
user_alias> name | user
But now I have a problem due my poor SQL level... How to join both tables to generate something like this:
1 | my_name | my_address | my_comments
1 | my_alias | my_address | my_comments
2 | other_name | other_address | other_comments
I mean, I want to make a "SELECT..." query that returns in the same format as the "user" table ALL users and ALL alias.. Something like this:
SELECT user.* FROM user LEFT JOIN user_alias ON `user`=`id`
but it doesn't work for me..
I think you need something like this:
SELECT user.*
FROM user
LEFT JOIN user_alias
ON user.name=user_alias.name
Your original query was not specific enough in the join condition.
Something like
SELECT user.name, user.address, user.comment FROM user
UNION ALL
SELECT user_alias.alias, user.address, user.comment
FROM user INNER JOIN user_alias on user.name = user_alias.name
ORDER BY name
will get you close to what you want.
You need to UNION two SELECTs together because the LEFT JOIN solution proposed by others will include only one row in the result set for users with aliases, not two as specified in your question.
But you should make the common column joining user and alias the id column, not the name column.
SELECT user.* FROM user LEFT JOIN user_alias ON user.name = user_alias.name
First of all - the query you want to build is not trivial, because you are trying to get some results spanned across more than one row. So I will offer you a proper solution in a fashion like it should be (read: in a way a database developer will do this :-).
First, you should modify your user_alias table so that it will contain id column but not the name. It is not good idea to join your tables using the name field. The reason for this is that there could be two Sarah Connors.
Then, you can get results from both tables using this query:
SELECT user.*, user_alias.*
FROM user LEFT JOIN user_alias
ON user.id=user_alias.id
This way you will get your results in such format:
id | name | address | comments | user
-------------------------------------------------------------
1 | Sarah Connor | Planet Earth | Nice woman | sarah_connor
2 | Sarah Connor | USA, NY | Mean woman | sarah_c
3 | John Connor | USA, NY | n00b | john123
In the situations when there are two or more records in user_alias table for the same person (equal id's), you will get something like this:
id | name | address | comments | user
-------------------------------------------------------------
4 | Bill Clinton | White House | President | bill
4 | Bill Clinton | White House | President | monica