In a left join, select row with value A, if not select row with value B - sql

This must be simple, but I think I'm lost. I have a table A:
name id
Tom 1
Barbara 2
Gregory 3
...and table B:
id nickname preferred
1 Spiderman 0
1 Batman 1
2 Powerpuff 0
3 Donald Duck 0
3 Hulk 1
How do I query the table to get a nickname when it is preferred (1), or any other nickname if preferred is not available.
So the result for Tom would be "Batman", while the result for Barbara would be "Powerpuff".

Just an immediate solution:
select a.id,
b.nickname
from a
join b on a.id = b.id and b.prefered = 1
union all
select a.id,
b.nickname
from a
join b on a.id = b.id and b.prefered = 0
where a.id not in(
select a.id
from a
join b on a.id = b.id and b.prefered = 1
)
Fiddle http://sqlfiddle.com/#!7/0b7db/1

Try below Query:
Which 1. selects row with value A, otherwise, 2. select row with value B
using LEFT JOIN,
SELECT A.name, B.nickname
FROM A
LEFT JOIN
(
SELECT MAX(preferred) AS preferred, id
FROM B
GROUP BY id
)AS B1
ON A.id = B1.id
LEFT JOIN B ON B.preferred = B1.preferred AND B.id = B1.id

If SQLite supported analytic functions then that would provide a fairly clean and convenient solution. No such luck, though. It does simplify the problem that you want either all the preferred nicknames for a given person (of which there will be at most one) or all the non-preferred ones. It is then fairly straightforward to use an inline view to distinguish between those cases and apply a suitable filter:
SELECT p.name, pn.nickname
FROM
person p
JOIN (
SELECT id, MAX(preferred) AS preferred
FROM person_nickname
GROUP BY id
) flag
ON p.id = flag.id
JOIN person_nickname pn
ON pn.id = flag.id AND pn.preferred = flag.preferred

Related

Is it possible to replace content from multiple foreign keys when doing a query?

I have the following tables:
TABLE PLAYER
id | name
1 | A
2 | B
3 | C
4 | D
TABLE PAIRINGS
id | player_a | player_b
1 | 3 |1
2 | 2 |4
Both columns in table Pairings are foreign keys to column id of table player.
My problem is, I would like to avoid making a query from code every time I want to know which is my player's name (like, Select name From Player Where Id = pairings.player_a). I have thought about adding Name as an extra columnd to Pairings table, but that would denormalize it.
Instead, it would be nice if I could get the names in just one query, like:
RESULT
player_a | player_b
C | A
B | D
Is it this possible? Thanks so much in advance.
You may join the PAIRINGS table to the PLAYER table, twice:
SELECT
p1.name AS player_a,
p2.name AS player_b
FROM PAIRINGS pr
INNER JOIN PLAYER p1
ON pr.player_a = p1.id
INNER JOIN PLAYER p2
ON pr.player_b = p2.id;
Demo
Don't do it! One of the points of using a relational database is that data is stored in only one place. That is a big convenience. Of course, there are exceptions, but these exceptions should have firm reasons.
In this case, just define a view:
CREATE VIEW vw_pairings AS
SELECT p.*, pa.name AS player_a_name,
pb.name AS player_b_name
FROM PAIRINGS p JOIN
PLAYER pa
ON p.player_a = pa.id JOIN
PLAYER pb
ON p.player_b = pb.id;
When you query from the view, you will see the names, along with all the other information in the PAIRINGS table.
Hope can help you
Select * Into #PLAYER From (
Select 1 [ID], 'A' [Name] Union All
Select 2 [ID], 'B' [Name] Union All
Select 3 [ID], 'C' [Name] Union All
Select 4 [ID], 'D' [Name]
) A
Select * Into #PAIRINGS From (
Select 1 [ID], 3 [PLAYER_A], 1 [PLAYER_B] Union All
Select 2 [ID], 2 [PLAYER_A], 4 [PLAYER_B]
) A
Select
P.ID, A.NAME, B.NAME
From #PAIRINGS P
Left Join #PLAYER A On A.ID = P.PLAYER_A
Left Join #PLAYER B On B.ID = P.PLAYER_B
You can create view, for avoid making query
Example
Create View vwPAIRINGS As
Select
P.ID, A.NAME, B.NAME
From #PAIRINGS P
Left Join #PLAYER A On A.ID = P.PLAYER_A
Left Join #PLAYER B On B.ID = P.PLAYER_B
After that, just select usual
Select * from vwPAIRINGS

SQL joining two tables with common row

I have 2 tables in sybase
Account_table
Id account_code
1 A
2 B
3 C
Associate_table
id account_code
1 A
1 B
1 C
2 A
2 B
3 A
3 C
I have this sql query
SELECT * FROM account_table account, associate_table assoc
WHERE account.account_code = assoc.account_code
This query will return 7 rows. What I want is to return the rows from associate_table that is only common to the 3 accounts like this:
account id account_code Assoc Id
1 A 1
2 B 1
3 C 1
Can anyone help what kind of join should I do?
SELECT b.id account_id,a.code account_code,a.id assoc_id
FROM associate a,
account b
WHERE a.code = b.code
AND a.id IN (SELECT a.id
FROM associate a,
account b
WHERE a.code = b.code
GROUP BY a.id
HAVING Count(*) = (SELECT Count(*)
FROM account));
NOTE: this query works only if you have unique values in Id and account_code columns in account table. And also, your associate_table should contain unique combination of (id, account,code). i.e., associate table should not contain (1,A) or any pair twice.
Try this
SELECT AC.ID,AC.account_code,ASS.ID
FROM account_table AC INNER JOIN associate_table AS ASS ON AC.account_code = ASS.account_code
OK so far answer is accepted I'll post simpler one:
SELECT *
FROM account_table AS account,
associate_table AS assoc
WHERE account.account_code = assoc.account_code
HAVING (
SELECT
COUNT(*)
FROM associate_table assoc_2
WHERE assoc_2.id = assoc.id
) = 3
here 3 is the number of codes account table has, if it's gonna be dynamic (changing over time),
you can use (SELECT COUNT(*) FROM account_table) instead of exact number. Also I'm sure it will be cached by database engine, so requires less resources

SELECT statement to exclude results through join table relationship

Given three tables like this:
fruit: taste: fruit_taste:
id name id name id fruit_id taste_id
-- ---- -- ---- -- -------- --------
1 apple 1 sweet 1 1 1
2 lime 2 tart 2 1 2
3 banana 3 2 2
4 lemon 4 4 2
How can I SELECT only the fruit which do have not been identified by the join table as having a sweet taste? My desired results should also include 'banana', since there is not a fruit_taste identifying it as sweet.
I tried (along with a ton of other things):
SELECT *
FROM fruit
LEFT OUTER JOIN fruit_taste
ON fruit_taste.fruit_id = fruit.id
WHERE (fruit_taste.taste_id != 1 OR fruit_taste.id IS null)
which almost works, but still gives me 'apple' in my result, since my apple is a Granny Smith, and is both sweet and tart. How can I exclude 'apple' in my query? Is my only option a subquery with NOT IN?
select *
from fruit
where id not in
(select ft.fruit_id
from fruit_taste ft
inner join taste t
on ft.taste_id = t.id
where t.name = 'sweet')
Aside from using NOT IN, you can also use LEFT JOIN. To filter out fruits that have no sweet taste, the total number of sweet for every fruit should be 0.
SELECT a.ID, a.Name
FROM fruit a
LEFT JOIN fruit_taste b
ON a.id = b.fruit_id
LEFT JOIN taste c
ON b.taste_id = c.id
GROUP BY a.ID, a.Name
HAVING SUM(IFNULL(c.name = 'sweet', 0)) = 0
SQLFiddle Demo
You can use a LEFT JOIN and avoid the subquery if you add the excluded taste to the JOIN filter and then exclude matching rows. This should generally be better for performance than using NOT IN on a subquery, but YMMV.
SELECT *
FROM fruit
LEFT JOIN fruit_taste
ON fruit_taste.fruit_id = fruit.id
AND fruit_taste.taste_id = 1
WHERE fruit_taste.id IS NULL

SQL Query, how to return elements not in other 2 tables

here is my data
movies table:
id title
10 Promise Land
13 Alive
14 Bruce Almighty
15 Decay
19 Malcom X
users table:
id username
1 Franck
2 Matt
archive table:
userid movieid
1 13
2 14
1 14
I'd like to get all the movies.id, movies.title that are not in the archive table for user id = 1.
I want to use JOINS (I don't want a select of select)
result should be:
id title
10 Promise Land
15 Decay
19 Malcom X
the following SQL fails:
SELECT a.id,a.title
FROM db.movies AS a
LEFT JOIN db.archive AS b ON a.id = b.movieid
LEFT JOIN db.users AS c ON c.id = b.userid
WHERE b.movieid IS NULL OR b.userid !=1;
Thanks
Using JOINS. You put the userid filter into the JOIN
SELECT
a.id,a.title
FROM
binews.movies AS a
LEFT JOIN
binews.archive AS b ON a.id = b.movieid AND b.userid <> 1
WHERE
b.movieid IS NULL;
However, you are actually asking "give my movies where they don't exists for this user in the archive table)
SELECT
a.id,a.title
FROM
binews.movies AS a
WHERE
NOT EXISTS (SELECT *
FROM
binews.archive AS b
WHERE
a.id = b.movieid AND b.userid <> 1);
This is more correct generally. In some cases you'll get multiple rows from a LEFT JOIN where a userid has used the same more than once. To correct this, you'll need DISTINCT which adds processing.
However, EXISTS removes this multiple row output.
See this for more: http://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left-join-is-null-sql-server/
In SQLServer2005+ you can use option with EXISTS and EXCEPT operators
SELECT *
FROM dbo.movies
WHERE EXISTS (
SELECT id
EXCEPT
SELECT movieid
FROM archive
WHERE userid = 1
)
Demo on SQLFiddle
OR option with NOT EXISTS AND INTERSECT operators
SELECT *
FROM dbo.movies
WHERE NOT EXISTS (
SELECT movieid
FROM archive
WHERE userid = 1
INTERSECT
SELECT id
)
Demo on SQLFiddle
select * from binews.movies
where id not in(select movieid from binews.archive where userid<>1 )
thanks to gbn. here is the solution with a correction "AND b.userid=1"
SELECT
a.id,a.title
FROM
binews.movies AS a
LEFT JOIN
binews.archive AS b ON a.id = b.movieid AND b.userid=1
WHERE
b.movieid IS NULL

Oracle SQL - Joining rows that don't exist?

Take these two example tables:
Table A
Name FirstOrLast
Jeremy First
Smith Last
Mark First
David First
Table B
Name
Jeremy
Smith
Mark
David
Jones
Jack
I would like the output to be this: Output only first names, but if Table A doesn't record if it's a first or last name, output it anyway. The correct output would be:
Jeremy
Mark
David
Jones
Jack
I've tried accomplishing this with an outer join, like so:
select
B.Name
from
A, B
where
A.name = B.name(+) and
A.FirstOrLast = 'First';
but no luck. How can I do this?
When you perform a left outer join and there is no record in A, a.firstOrLast will be null. While you're at it, you might as well switch to the standard syntax for joins:
select
B.Name
from
B
left outer join A on A.name = B.name
where
A.firstOrLast is null or A.firstOrLast = 'First';
However, the old syntax should also work:
select
B.Name
from
A, B
where
A.name = B.name(+) and
(A.firstOrLast is null or A.firstOrLast = 'First');
Try this:
SELECT B.Name
FROM B LEFT JOIN A ON B.Name = A.Name
WHERE A.FirstOrLast = 'First' OR A.Name IS NULL;
select b.name
from b
union
select a.namfe
from a
where a.firstOrLast = 'First'
SELECT Name
FROM B
MINUS
SELECT Name
FROM A
WHERE FirstOrLast <> 'First'