T-SQL: Get all, but join on 'not null' - sql-server

So I'm trying to get all items from a table, and then add some data if there is a joined member, otherwise keep the data empty (null)
What I have is this:
SELECT
t.id AS _id,
m.name AS name
FROM
tableT AS t
INNER JOIN
tableM AS m ON t.m_id = m.id
The tables look like this:
tableT:
id m.id
----------------
1 NULL
2 1
3 NULL
tableM:
id name
----------------
1 'Bob'
The desired result should be this:
id name
------------------
1 NULL
2 'Bob'
3 NULL
How can I achieve this, as far as I can see inner join won't join on NULL values since they can't be matched

You're looking for a LEFT JOIN
SELECT
t.id AS _id,
m.name AS name
FROM tableT AS t
LEFT JOIN
tableM AS m
ON t.m_id = m.id
The definition of an INNER JOIN will only return data where there is a match in both tables, if one table has missing data then that row will not be returned.
A LEFT JOIN will get all data from the first table and only matching records in the second table, allowing for NULL values.
A little further reading if it's useful;
http://www.w3schools.com/sql/sql_join_left.asp

use LEFT JOIN:
SELECT
t.id AS _id,
m.name AS name
FROM tableT AS t
LEFT OUTER JOIN
tableM AS m
ON t.m_id = m.id

Your requirement is Left Join:
SELECT
t.id AS _id,
m.name AS name
FROM tableT AS t
LEFT OUTER JOIN
tableM AS m
ON t.m_id = m.id
Please check below link for more information regarding different type of joins.
https://msdn.microsoft.com/en-us/library/zt8wzxy4.aspx

Related

Avoid select data from other tables

I'm coding a movies web app, for showing some tags I'm trying to write a query that returns the movie name and the count of how many categorys has assigned it. I'm trying to add a filter that for example: if X movie contains a "Comedy" category this movie doesn't even need to be consider in my query.
At the moment this is the query that I have:
SELECT A.*, B.*, C.* -- A.name, count(C.name) [Categories]
FROM movies A
INNER JOIN moviesGenres B ON A.id = B.movieId
INNER JOIN genres C ON B.genreId = C.id
WHERE C.name <> 'Comedy'
-- group by A.name, C.name
-- having count(C.name) > 2
At the moment this query is working to return the expected output. But if you run that query with the data in this SQL Fiddle you'll see that is considering the movie "Bad Boys" but this movie has assigned one "Comedy" category so any data from this movie should't be considered.
You need to put that condition in the having clause wich is the where clause of a group.
SELECT m.name, count(g.name) [Genres]
FROM movies m
INNER JOIN moviesGenres mg ON m.id = mg.movieId
INNER JOIN genres g ON mg.genreId = g.id
GROUP BY m.name
HAVING sum(case when g.name = 'Comedy' then 1 else 0 end) = 0
If you really want to select columns from [moviesGenres] and [genres], go for juergen's answer. If you don't need them, anti join them:
SELECT A.*, B.*, C.* -- A.name, count(C.name) [Categories]
FROM
movies A
WHERE NOT EXISTS
(
SELECT 1
FROM
moviesGenres Bneg
inner join genres Cneg ON Bneg.genreId = Cneg.id
WHERE
A.id = Bneg.movieId
and Cneg.name = 'Comedy'
)

SQL to find which table has diag_code filled, and then look it up in lookup_table

I have a query and a diag_code is either in one table (UM_SERVICE) or the other (LOS), but I can't join both tables to get diag_code that isn't null, that I can think of. Does this look ok for finding if diag_code is in one of the tables and lookup table? It's possible to have both LOS and UM_SERVICE have a diag code on different rows, and they could be different, and both or one could be in the lookup table. I'm not seeing anything in internet search.
Here's a simplified stored procedure:
SELECT distinct
c.id
,uc.id
,c.person_id
FROM dbo.CASES c
INNER JOIN dbo.UM_CASE uc with (NOLOCK) ON uc.case_id = c.id
LEFT JOIN dbo.UM_SERVICE sv (NOLOCK) ON sv.case_id = omc.case_id
LEFT JOIN dbo.UM_SERVICE_CERT usc on usc.service_id = sv.id
LEFT JOIN dbo.LOS S WITH (NOLOCK) ON S.case_id = UC.case_id
LEFT JOIN dbo.LOS_EXTENSION SC WITH (NOLOCK) ON SC.los_id = S.id
INNER JOIN dbo.PERSON op with (NOLOCK) on op.id = c.Person_id
WHERE
(sv.diag_code is not null and c.case_id = sv.case_id
or
s.diag_code is not null and c.case_id = s.case_id)
and
(sv.diag_code is not null and sv.diag_code in (select diag_code from TABLE_LOOKUP)
or
s.diag_code is not null and s.diag_code in (select diag_code from TABLE_LOOKUP)
Table setups like this:
CASES
id person_id
UM_CASE
case_id
LOS
case_id id
LOS_EXTENSION
los_id
Person
id cid
UM_SERVICE
case_id diag_code
UM_SERVICE_CERT
service_id id
TABLE_LOOKUP
diag_code
Since you have two different searches being run, it is going to be much easier to write/read by writing the searches individually and then bringing your two results sets together using the UNION operator. The UNION will eliminate duplicates across the two result sets in a similar manner to what your usage of SELECT DISTINCT is doing for a single result set.
Like so:
/*first part of union performs seach using filter on dbo.UM_SERVICE*/
SELECT
c.id
,uc.id
,c.person_id
FROM
dbo.CASES AS c
INNER JOIN dbo.UM_CASE AS uc ON uc.case_id=c.id
LEFT JOIN dbo.UM_SERVICE AS sv ON sv.case_id = omc.case_id
LEFT JOIN dbo.UM_SERVICE_CERT AS usc on usc.service_id=sv.id
LEFT JOIN dbo.LOS AS S ON S.case_id = UC.case_id
LEFT JOIN dbo.LOS_EXTENSION AS SC ON SC.los_id= S.id
INNER JOIN dbo.PERSON AS op on op.id=c.Person_id
WHERE
sv.diag_code in (select diag_code from TABLE_LOOKUP) /*will eliminate null values in sv.diag_code*/
UNION /*deduplicate result sets*/
/*second part of union performs search using filter on dbo.LOS*/
SELECT
c.id
,uc.id
,c.person_id
FROM
dbo.CASES AS c
INNER JOIN dbo.UM_CASE AS uc ON uc.case_id=c.id
LEFT JOIN dbo.UM_SERVICE AS sv ON sv.case_id = omc.case_id
LEFT JOIN dbo.UM_SERVICE_CERT AS usc on usc.service_id=sv.id
LEFT JOIN dbo.LOS AS S ON S.case_id = UC.case_id
LEFT JOIN dbo.LOS_EXTENSION AS SC ON SC.los_id= S.id
INNER JOIN dbo.PERSON AS op on op.id=c.Person_id
WHERE
s.diag_code in (select diag_code from TABLE_LOOKUP); /*will eliminate null values in s.diag_code*/

Multiple Nested Inner Joins: not all records are shown

I have difficulty joining two tables that look like the following:
The main table PMEOBJECT which has a unique key named OBJECTID and
has in total 12768 rows.
Then I want to join PMEOBJECTVALIDITY on it which has an n:1 relationship with PMEOBJECT, since it has more rows,
because it saves the changes over time of PMEOBJECT (i.e. when a certain object is not
valid anymore), this one has 12789 rows (meaning only 21 objects
changed over time). However, I only want to have the current last
VALIDFROM date shown in the query. This all works fine.
Then the trouble starts when I want to join PMEOBJECTDIMENSION, which has an
n:1 relationship with PMEOBJECTVALIDITY and has 36737 rows in total.
SELECT
PMEOBJECT.OBJECTID
,PMEOBJECTVALIDITY.VALIDFROM
,PMEOBJECTDIMENSION.DIMENSION2_
FROM PMEOBJECT
LEFT JOIN PMEOBJECTVALIDITY
ON PMEOBJECTVALIDITY.OBJECTID = PMEOBJECT.OBJECTID
AND PMEOBJECTVALIDITY.DATAAREAID = PMEOBJECT.DATAAREAID
INNER JOIN(
SELECT
OBJECTID,
MAX(VALIDFROM) AS NEWFROMDATE,
MAX(VALIDTO) AS NEWTODATE
FROM PMEOBJECTVALIDITY B
GROUP BY OBJECTID
) B
ON PMEOBJECTVALIDITY.OBJECTID = B.OBJECTID
AND PMEOBJECTVALIDITY.VALIDFROM = B.NEWFROMDATE
LEFT JOIN PMEOBJECTDIMENSION
ON PMEOBJECTDIMENSION.OBJECTVALIDITYID = PMEOBJECTVALIDITY.RECID
AND PMEOBJECTDIMENSION.DATAAREAID = PMEOBJECTVALIDITY.DATAAREAID
INNER JOIN(
SELECT
OBJECTVALIDITYID,
MAX(VALIDFROM) AS NEWFROMDATE_2
FROM PMEOBJECTDIMENSION C
GROUP BY OBJECTVALIDITYID
) C
ON PMEOBJECTDIMENSION.OBJECTVALIDITYID = C.OBJECTVALIDITYID
AND PMEOBJECTDIMENSION.VALIDFROM = C.NEWFROMDATE_2
Results in query per step:
SELECT PMEOBJECT: 12768 rows
LEFT JOIN PMEVALIDITY: 12789 rows
INNER JOIN PMEVALIDITY: 12768 rows
LEFT JOIN PMEOBJECTDIMENSION: 36737 rows
INNER JOIN PMEOBJECTDIMENSION: 12729 rows
I want the end result again to have the same 12768 rows, I don't want any ObjectId to be left out.
What am I missing here?
Kind regards,
Igor
Following might help:
from PMEOBJECTDIMENSION onwards:
LEFT JOIN (SELECT PMEOBJECTDIMENSION.OBJECTVALIDITYID, PMEOBJECTDIMENSION.DATAAREAID
FROM PMEOBJECTDIMENSION
INNER JOIN(SELECT OBJECTVALIDITYID, MAX(VALIDFROM) AS NEWFROMDATE_2
FROM PMEOBJECTDIMENSION C
GROUP BY OBJECTVALIDITYID
) C
ON PMEOBJECTDIMENSION.OBJECTVALIDITYID = C.OBJECTVALIDITYID
AND PMEOBJECTDIMENSION.VALIDFROM = C.NEWFROMDATE_2
)X
ON X.OBJECTVALIDITYID = PMEOBJECTVALIDITY.RECID
AND X.DATAAREAID = PMEOBJECTVALIDITY.DATAAREAID
and select the distinct records if duplicates present.
The INNER JOINs are filtering out records- what you want is that the LEFT JOIN table (PMEOBJECTVALIDITY and PMEOBJECTDIMENSION) should only include records that have at least a match on the INNER JOIN queries (alias B and C). You can accomplish this with by nesting the INNER JOIN with the LEFT JOIN, generally done as follows:
SELECT *
FROM A
LEFT JOIN B
INNER JOIN C
ON B.ID = C.BID
ON A.ID = B.AID
Now B is INNER JOINed on C and will only contain records that have a match in C, but will preserve the LEFT JOIN not remove any records from A.
In your case, you can simply move the ON clause from the LEFT JOIN to the end of the following INNER JOIN.
SELECT
PMEOBJECT.OBJECTID
,PMEOBJECTVALIDITY.VALIDFROM
,PMEOBJECTDIMENSION.DIMENSION2_
FROM PMEOBJECT
LEFT JOIN PMEOBJECTVALIDITY
INNER JOIN(
SELECT
OBJECTID,
MAX(VALIDFROM) AS NEWFROMDATE,
MAX(VALIDTO) AS NEWTODATE
FROM PMEOBJECTVALIDITY B
GROUP BY OBJECTID
) B
ON PMEOBJECTVALIDITY.OBJECTID = B.OBJECTID
AND PMEOBJECTVALIDITY.VALIDFROM = B.NEWFROMDATE
ON PMEOBJECTVALIDITY.OBJECTID = PMEOBJECT.OBJECTID
AND PMEOBJECTVALIDITY.DATAAREAID = PMEOBJECT.DATAAREAID --here it is!
LEFT JOIN PMEOBJECTDIMENSION
INNER JOIN(
SELECT
OBJECTVALIDITYID,
MAX(VALIDFROM) AS NEWFROMDATE_2
FROM PMEOBJECTDIMENSION C
GROUP BY OBJECTVALIDITYID
) C
ON PMEOBJECTDIMENSION.OBJECTVALIDITYID = C.OBJECTVALIDITYID
AND PMEOBJECTDIMENSION.VALIDFROM = C.NEWFROMDATE_2
ON PMEOBJECTDIMENSION.OBJECTVALIDITYID = PMEOBJECTVALIDITY.RECID
AND PMEOBJECTDIMENSION.DATAAREAID = PMEOBJECTVALIDITY.DATAAREAID --I'm here

SQL UNION INNER JOIN

Im trying to select from 2 tables that have the same columns, but both tables have an inner join -
select e.ID,
c.FullName,
car.VIN,
car.Registration,
e.Telephone,
e.Mobile,
e.Email,
e.EstimateTotal,
e.LastUpdated,
e.LastUpdateBy from (select id from Estimates UNION ALL select id from PrivateEstimates) e
inner join Customers c on c.ID = e.CustomerID
inner join Cars car on car.ID = e.CarID
where e.Status = 0
The trouble is, it can't find e.CustomerID, e.CarID or e.Status on the inner join? Any ideas?
Your subquery
select id from Estimates
union all
select id from PrivateEstimates
returns only a single id column. Include necessary columns in the subquery if you want to use those columns in JOIN statements

SQL Server SELECT composite table

I have the below query:
SELECT p.id as prod_id, * FROM products AS p
LEFT JOIN Product_UPC AS UPC ON UPC.ProductID = p.id
LEFT JOIN Brands AS b ON p.brand = b.id
LEFT JOIN productCategoryLink AS c ON c.ProductID = p.id
WHERE (p.id = '$this->prod_id')
A product can be assigned to multiple categories and therefore I have a composite table consisting of product and category IDs. I want to amend the above query so that it only brings out one row of data as at the moment it bring out multiple depending on how many categories there are in the composite table. I would like to somehow have the rows of category IDs brought out and added to the one row.
eg.
id | name | desc | category1| category2| category3 | price
Is this possible? If so how?
You could try this to obtain categories in one column (you can split them later):
SELECT p.id as prod_id, * FROM products AS p
GROUP_CONCAT(c.category),
LEFT JOIN Product_UPC AS UPC ON UPC.ProductID = p.id
LEFT JOIN Brands AS b ON p.brand = b.id
LEFT JOIN productCategoryLink AS c ON c.ProductID = p.id
WHERE (p.id = '$this->prod_id')
If you want to get all products, change last row with this
GROUP BY p.id

Resources