select row from table and substitute a field with one from another column if it exists - sql

I'm trying construct a PostgreSQL query that does the following but so far my efforts have been in vain.
Problem:
There are two tables: A and B. I'd like to select all columns from table A (having columns: id, name, description) and substitute the "A.name" column with the value of the column "B.title" from table B (having columns: id, table_A_id title, langcode) where B.table_A_id is 5 and B.langcode is "nl" (if there are any rows).
My attempts:
SELECT A.name,
case when exists(select title from B where table_A_id = 5 and langcode= 'nl')
then B.title
else A.name
END
FROM A, B
WHERE A.id = 5 and B.table_A_id = 5 and B.langcode = 'nl'
-- second try:
SELECT COALESCE(B.title, A.name) as name
from A, B
where A.id = 5 and B.table_A_id = 5 and exists(select title from B where table_A_id = 5 and langcode= 'nl')
I've tried using a CASE and COALESCE() but failed due to my inexperience with both concepts.
Thanks in advance.

araqnid's is the answer you are looking for, I bet.
But if you want to enforce that no more than one row is returned for each original matching A row, you might prefer to do a subselect instead of a LEFT JOIN. For example:
SELECT A.id, COALESCE(
( SELECT max(B.title) FROM B WHERE
langcode = 'nl' AND B.table_a_id = A.id), A.name ) as name
FROM A
WHERE A.id = 5
I use "max" here to select an arbitrary value, in the event there is more than one. You can use "min" or whatever you consider appropiate in your case.
Perhaps this is more easy to understand than the LEFT JOIN, but (apart from the two being not exactly equivalent) a JOIN will perform better than N subselects (much better is N is large).
Anyway, from a learning point of view, it's good to understand both.

select A.id, coalesce(B.title, A.name)
from TableA AS A
left join (select table_a_id, title from TableB where langcode = 'nl') AS B
on B.table_a_id = A.id
WHERE A.id = 5

Ok, I'm not sure how your tables have to be joined, but something like this should do the job:
SELECT yourcolumnlist,
CASE WHEN A.name IS NULL THEN B.title ELSE A.name END
FROM TableA AS A
INNER JOIN TableB AS B
ON A.id = B.table_A_id
WHERE B.table_A_id = 5
AND B.langcode = 'nl'
Another way to do it would be to use the COALESCE() function:
SELECT yourcolumnlist,
COALESCE(A.name, B.title)
FROM TableA AS A
INNER JOIN TableB AS B
ON A.id = B.table_A_id
WHERE B.table_A_id = 5
AND B.langcode = 'nl'

Try this
select A.id,B.title,A.description from TableA as A inner join tableB as B
on A.id=B.id where B.table_A_id = 5 and B.langcode ='nl'

Related

Select one record from left table, but multiple from right table

I have two tables, where I want to select all the data where it matches on identifier, however I want to replace the repeated data collected from table A to NULL. My current result is shown in the Current Table (refer to attached image). I want results displayed as shown in New Table.
My Current SQL is:
SELECT
a.name, a.desc, b.seq
from table_a a, table_b b
where a.name = b.name
I believe I can possibly do this by using CASE:
CASE
WHEN
b.seq = 1 THEN a.name, a.desc ELSE a.name IS NULL, a.desc IS NULL
END
But I'm not having any luck. Any help please?
Thank you!
Use the CASE clause:
select
case when b.seq = 1 then a.name end,
case when b.seq = 1 then a.desc end,
b.seq
from table_a a
join table_b b on a.name = b.name

Compare table one value with two different tables in postgresql

There are three tables, A,B and C having common columns(name and number)
Table A have 10 records(say x) which can be only from table B(say, y) and table C(say, z) (like, x = y+z).
In table A, there are some records whose value is 0 (zero)
I need to compare those zero value based records using column = name, with other two tables.
And check the column "number" for the same "name" is also zero (0) in table B and table C?
I tried to write the below sample query to test on my small set of 3 tables data- but for some reasons I am not able to get all the 10 records as a result?
SELECT a.name,a.number as A_number, b.number as B_number, c.number as C_number
from A a, B b, C c
WHERE a.name = b.name
The above query gives me data as follows in the sqlfiddle-
http://sqlfiddle.com/#!2/57f86/1
In the above data- theres no record name="hello"
Can anyone please correct me where I am going wrong? and how to get the exact result? I need all the records from Table A. I know if I use left join it will populate all the left table data even if no match.
Possibilities: Table A having records, some may be present in table B
and some in table C, but not on both.
I think this is what you want:
SELECT a.*, b.number as bnumber, c.number as cnumber
from a left outer join
b
on a.name = b.name left outer join
c
on a.name = c.name
where a.number = 0;
By the way, here is a Postgres SQL Fiddle.
It's been over 20 years since the JOIN keyword was added to SQL. Use it:
select
a.name,
a.number as A_number,
b.number as B_number,
c.number as C_number
from A a
left join B b on a.name = b.name
left join C c on a.name = c.name
where a.number = 0
The key here is the use of left join, which allows all rows in table A to be returned, even if there are no matching rows in the other tables.
If you want to just display true/false if the number is zero in the other tables, do this:
select
a.name,
a.number as A_number,
(b.number = 0 and c.number = 0) as zero_elsewhere
from A a
left join B b on a.name = b.name
left join C c on a.name = c.name
where a.number = 0
When you wrote WHERE a.name = b.name, that restricted the records returned from table A to only those that also exist in table B. This is not equivalent to a left join. If you used only a WHERE statement you would need to do:
WHERE ((a.name = b.name) OR (b.name is NULL))
AND
((a.name = c.name) OR (c.name is NULL))
In the comments and other answers, they have been using LEFT JOIN which is easier to write and read. I suggest you adopt that style as it is widely accepted.

Getting data from one table to another table using join

I have a table name "a"
Id name
1 abc
2 xyz
3 mmm
4 xxx
and Other table name is "b"
Id suId
3 2
3 1
My requirement is get detail from "a" table from "b" table where id=3. Any help?
SELECT a.Id, a.name, b.Id, b.suId FROM b JOIN a ON b.suId = a.Id WHERE b.Id = 3;
I wont recommend join for this kind of scenarios(you want all details from Table A whose ids are in Table B suId column, where Table B id should be 3., Its bad English but hope you got me and may be i got you too.)
SELECT a.name FROM a
WHERE
a.id IN(SELECT b.suId FROM b WHERE b.id = 3);
If you want to use join only then,
SELECT a.name FROM a,b
WHERE a.id = b.suId
AND
b.id = 3;
Simple answer:
SELECT a.Id,a.name FROM a,b
WHERE a.Id=b.suId AND b.Id=3
It will give you the result:
Id Name
1 abc
2 xyz
See result in SQL Fiddle
This should get the job done:
SELECT * FROM table_a a JOIN table_b b ON b.suId = a.Id WHERE b.Id = 3;
You can try this...You can try different JOIN clauses like INNER JOIN, LEFT OUTER JOIN or just simply JOIN etc. You will get different number of rows depending on field connections from 1 table to the other.
SELECT T1.*
FROM a T1
INNER JOIN b T2
ON T1.Id = T2.Id
WHERE T1.Id='3'

SQL Query efficiency (JOIN or Cartesian Product )

Hello I am Confused with three scenarios which commonly every one use in almost every project.
I wanted to Know which one of these will be Efficient accordinng to - Less Time Complexity - Efficiency - effectiveness
TableA (userid ,username, email , phone)
TableB (username,TestField)
.
Case 1
select email, TestField from TableA , TableB
where TableA.username = TableB.username and
TableB.username = 'ABC'
group by email, TestField
Case 2
select email, TestField from TableA
inner join TableB on TableB.username = 'ABC'
Case 3
declare #uname nvarchar(20);
set #uname = 'ABC';
declare #Email nvarchar(20);
select #Email= email from TableA where username = #uname;
select #Email as email , TestField from TableB
where username = #uname
Case 2 will give you a different output anyway, as you are not joining TableA and TableB in any way so you get a Cartesian product.
Since all of a sudden email came up, you will need a join in case 1:
In Case 1 you can simply rewrite the query to
SELECT DISTINCT A.Email , B.TestField
FROM TableA A join TableB B on A.username = B.Username
WHERE B.username = 'ABC'
Which is more readable and easier to maintain as you do not ave a superfluous GROUP BY clause.
In Case 3 you have userId in your where clause, which is not even in your tableB according to your post.
In general, for maintainability and readibility:
Use explicit joins
SELECT * FROM A JOIN B ON A.id = B.id
is preferable over
SELECT * FROM A, B WHERE A.id = B.id
And use DISTINCT when you want distinct values, instead of GROUP BY over all columns:
SELECT DISTINCT a, b, b FROM TABLE
is preferable over
SELECT a, b, c FROM TABLE GROUP BY a, b, c
Most database experts will tell you that cross products are evil and to be avoided. Your first example would work just fine. It is an implicit inner join.
Your second example is syntactically incorrect. I suspect you'd get an error from MSSQL Server Manager. What you probably meant was:
select a.email, b.TestField
from TableA a inner join TableB b
on (b.username = a.username)
where b.username = 'ABC'
Your first example will probably be the more efficient, since MSSQL Server is smart enough to do the projection on TableB.username before doing the join. I'm not so certain that this would be the case in the above version of case 2.
To be sure you could do it like this:
select a.email, b.TestField
from TableA a inner join
(select * from TableB where TableB.username = 'ABC') b
on (b.username = a.username)
where b.username = 'ABC'
Hope that helps.

Simulate a left join without using "left join"

I need to simulate the left join effect without using the "left join" key.
I have two tables, A and B, both with id and name columns. I would like to select all the dbids on both tables, where the name in A equals the name in B.
I use this to make a synchronization, so at the beginning B is empty (so I will have couples with id from A with a value and id from B is null). Later I will have a mix of couples with value - value and value - null.
Normally it would be:
SELECT A.id, B.id
FROM A left join B
ON A.name = B.name
The problem is that I can't use the left join and wanted to know if/how it is possible to do the same thing.
you can use this approach, but you must be sure that the inner select only returns one row.
SELECT A.id,
(select B.id from B where A.name = B.name) as B_ID
FROM A
Just reverse the tables and use a right join instead.
SELECT A.id,
B.id
FROM B
RIGHT JOIN A
ON A.name = B.name
I'm not familiar with java/jpa. Using pure SQL, here's one approach:
SELECT A.id AS A_id, B.id AS B_id
FROM A INNER JOIN B
ON A.name = B.name
UNION
SELECT id AS A_id, NULL AS B_id
FROM A
WHERE name NOT IN ( SELECT name FROM B );
In SQL Server, for example, You can use the *= operator to make a left join:
select A.id, B.id
from A, B
where A.name *= B.name
Other databases might have a slightly different syntax, if such an operator exists at all.
This is the old syntax, used before the join keyword was introduced. You should of course use the join keyword instead if possible. The old syntax might not even work in newer versions of the database.
I can only think of two ways that haven't been given so far. My last three ideas have already been given (boohoo) but I put them here for posterity. I DID think of them without cheating. :-p
Calculate whether B has a match, then provide an extra UNIONed row for the B set to supply the NULL when there is no match.
SELECT A.Id, A.Something, B.Id, B.Whatever, B.SomethingElse
FROM
(
SELECT
A.*,
CASE
WHEN EXISTS (SELECT * FROM B WHERE A.Id = B.Id) THEN 1
ELSE 0
END Which
FROM A
) A
INNER JOIN (
SELECT 1 Which, B.* FROM B
UNION ALL SELECT 0, B* FROM B WHERE 1 = 0
) B ON A.Which = B.Which
AND (
A.Which = 0
OR (
A.Which = 1
AND A.Id = b.Id
)
)
A slightly different take on that same query:
SELECT A.Id, B.Id
FROM
(
SELECT
A.*,
CASE
WHEN EXISTS (SELECT * FROM B WHERE A.Id = B.Id) THEN A.Id
ELSE -1 // a value that does not exist in B
END PseudoId
FROM A
) A
INNER JOIN (
SELECT B.Id PseudoId, B.Id FROM B
UNION ALL SELECT -1, NULL
) B ON A.Which = B.Which
AND A.PseudoId = B.PseudoId
Only for SQL Server specifically. I know, it's really a left join, but it doesn't SAY LEFT in there!
SELECT A.Id, B.Id
FROM
A
OUTER APPLY (
SELECT *
FROM B
WHERE A.Id = B.Id
) B
Get the inner join then UNION the outer join:
SELECT A.Id, B.Id
FROM
A
INNER JOIN B ON A.name = B.name
UNION ALL
SELECT A.Id, NULL
FROM A
WHERE NOT EXISTS (
SELECT *
FROM B
WHERE A.Id = B.Id
)
Use RIGHT JOIN. That's not a LEFT JOIN!
SELECT A.Id, B.Id
FROM
B
RIGHT JOIN A ON B.name = A.name
Just select the B value in a subquery expression (let's hope there's only one B per A). Multiple columns from B can be their own expressions (YUCKO!):
SELECT A.Id, (SELECT TOP 1 B.Id FROM B WHERE A.Id = B.Id) Bid
FROM A
Anyone using Oracle may need some FROM DUAL clauses in any SELECTs that have no FROM.
You could use subqueries, something like:
select a.id
, nvl((select b.id from b where b.name = a.name), "") as bId
from a
you can use oracle + operator for left join :-
SELECT A.id, B.id
FROM A , B
ON A.name = B.name (+)
Find link :-
Oracle "(+)" Operator
SELECT A.id, B.id
FROM A full outer join B
ON A.name = B.name
where A.name is not null
I'm not sure if you just can't use a LEFT JOIN or if you're restricted from using any JOINS at all. But as far as I understand your requirements, an INNER JOIN should work:
SELECT A.id, B.id
FROM A
INNER JOIN B ON A.name = B.name
Simulating left join using pure simple sql:
SELECT A.name
FROM A
where (select count(B.name) from B where A.id = B.id)<1;
In left join there are no lines in B referring A so 0 names in B will refer to the lines in A that dont have a match
+ or A.id = B.id in where clause to simulate the inner join