SQL Server join where not exist on other table - sql

+-------------------+ +-------------------+ +---------------------+
| Service | | Asset | | AssetService |
+-------------------+ +-------------------+ +---------------------+
| Id | Name | | Id | Name | | AssetId | ServiceId |
|-------------------| |-------------------| |---------------------|
| 1 | Service 1 | | 1 | Asset 1 | | 1 | 1 |
| 2 | Service 2 | | 2 | Asset 2 | | 1 | 2 |
| 3 | Service 3 | | 3 | Asset 3 | | 2 | 2 |
+-------------------+ +-------------------+ | 2 | 3 |
+---------------------+
So I have these tables. I want to get the Services that is not on AssetService where AssetId = 1
Like this:
+-------------------+
| Service |
| Id | Name |
+-------------------+
| 3 | Service 3 |
+-------------------+
Is this possible with just inner/left/right join? because I already tried different combinations of inner join but it's not working, like this inner join Asset a on a.Id != as.AssetId. I event tried left and right join.
Can somebody help me?
Thanks.

You can you use an intelligent left join to return non-matching rows only from left table(Service)
SELECT S.Id, S.Name FROM [Service] S
LEFT JOIN ServiceAsset SA
ON S.Id = SA.ServiceId
WHERE SA.ServiceId IS NULL
Note: INNER JOIN returns the matching rows whereas you want the non matching rows then use LEFT JOIN instead

The simplest I can think of:
select * from Service
where Id not in (
select ServiceId
from AssetService
where AssetId = 1);
SQLFiddle link
I don't think it's possible using inner join, because that would only retrieve records that match some criteria and you are looking for records that do not match.
It is, however, possible to do it with left join as Ctznkane525 shows in his answer.
Edit
As jarlh pointed out in the comments, not in might lead to surprising results when there are nulls in the subquery. So, here is the not exists version:
select Id, Name
from Service s
where not exists (
select *
from AssetService a
where AssetId = 1
and ServiceId = s.Id);
SQLFiddle link

Try this:
select * from Service where Id not in (
select ServiceId from AssetService where AssetId = 1
-- we have to filter out NULLs, in case of NULL values query result will be empty
and ServiceId not null
)
It doesn't require any join.
Here is solution with join:
select Id, Name from Service
except
select S.Id, S.Name from Service S join AssetService [AS] on S.Id = [AS].ServiceId
where [AS].AssetId = 1

Related

What's the best way to create empty/default rows for missing aggregations

I have a table I want to group over two levels. As the output, I need all the grouping value combinations, such that I end up with zeros where non existant combinations occur. For example, say I have this table:
+------+------+
| user | page |
+------+------+
| a | 1 |
| a | 1 |
| a | 2 |
| b | 2 |
| b | 3 |
+------+------+
I'm after output like this:
+------+------+--------+
| user | page | visits |
+------+------+--------+
| a | 1 | 2 |
| a | 2 | 1 |
| a | 3 | 0 |
| b | 1 | 0 |
| b | 2 | 1 |
| b | 3 | 1 |
+------+------+--------+
I can achieve this with the following query, but it seems rather heavy handed:
WITH
users AS (SELECT distinct(user) FROM sometable),
pages AS (SELECT distinct(page) FROM sometable),
users_pages_empty AS (SELECT * FROM users CROSS JOIN pages),
users_pages_full AS (SELECT user, page, count(*) as visits FROM sometable GROUP BY user, page)
SELECT e.user, e.page, coalesce(f.visits, 0) as visits
FROM users_pages_empty e
LEFT JOIN users_pages_full f ON e.user=f.user AND e.page=f.page
I happen to be using AWS Athena, but I think this is more a generic SQL question than an Athena question.
The performance of this query is fine, it's more the readability/complexity I'm not happy with.
Use a cross join to generate the rows and a left join to bring in the existing rows and aggregate:
select u.user, p.page, count(s.user)
from (select distinct user from sometable) u cross join
(select distinct page from sometable) p left join
sometable s
on s.user = u.user and s.page = p.page
group by u.user, p.page
order by u.user, p.page;

How to exclude entire Customer ID data based on a condition

I need to exclude all data relating to Customer ID's that have at least one instance where a condition applies. For instance:
This is all my data with conditions
+-------------+----------------+
| Customer ID | Condition |
+-------------+----------------+
| 1 | Contacted |
| 1 | No Answer |
| 1 | Left Voicemail |
| 1 | Spoke to |
| 2 | No Answer |
| 2 | Left Voicemail |
| 3 | Spoke to |
| 3 | No Answer |
| 4 | Contacted |
| 4 | Left Voicemail |
+-------------+----------------+
I need to exclude data with conditions equal to 'Contacted'. Currently, I am using the below code and getting the following results:
SELECT a.customerID,
c.condition
FROM Tablea a
JOIN Tablec c ON
c.customerID = a.customerID
WHERE c.condition NOT LIKE 'Contacted'
+-------------+----------------+
| Customer ID | Condition |
+-------------+----------------+
| 1 | No Answer |
| 1 | Left Voicemail |
| 1 | Spoke to |
| 2 | No Answer |
| 2 | Left Voicemail |
| 3 | Spoke to |
| 3 | No Answer |
| 4 | Left Voicemail |
+-------------+----------------+
However, I would like to exclude all of the Customer ID rows if the Customer ID has the condition. Ideally the code would produce the following:
+-------------+----------------+
| Customer ID | Condition |
+-------------+----------------+
| 2 | No Answer |
| 2 | Left Voicemail |
| 3 | Spoke to |
| 3 | No Answer |
+-------------+----------------+
Any help is greatly appreciated!
This will get you the results you are looking for:
Select customerid, condition from
Tablec c
WHERE customerid NOT IN
(
Select customerid from
Tablec
WHERE condition LIKE 'Contacted'
)
In your above example I am uncertain as to why you are joining to Tablea, as you aren't pulling any data from that table except for customerID, which is already in Tablec.
However if you wanted to do the join, you could do:
Select a.customerID, c.condition from Tablea a
JOIN Tablec c ON c.customerID = a.customerID
WHERE c.customerid NOT IN
(
Select customerid from
Tablec
WHERE condition LIKE 'Contacted'
)
Here's one option using not exists:
select customerid, condition
from Tablec c
where not exists (
select 1
from Tablec c2
where c.customerid = c2.customerid and c2.condition = 'Contacted'
)
This only uses Tablec since it appears to have both customerid and condition. You can always join it back to TableA as needed.
This is slightly hacky but I'm not sure what you're original dataset looks like.
SELECT a.customerID,
c.condition
FROM Tablea a
INNER JOIN Tablec c ON
c.customerID = a.customerID
WHERE c.customerID NOT IN
(
SELECT a2.customerID
FROM Tablec a2
WHERE a2.condition = 'Contacted'
)

LEFT JOIN Duplicating Rows

Table 1 has the join field (fieldY) duplicated many times within this table although every row in totality is unique.
When I try to run a left join I am getting 20x more rows than expected. I have tried to use solutions this post with no luck.
My expectation is that the join would yield exactly as many rows as table1 without the join. The join would just bring in one more column (fieldX)
Any ideas?
SELECT
table1.*, table2.fieldZ
FROM
table1
LEFT JOIN
table2
ON
table2.fieldX = table1.fieldY
WHERE
criteria1 = '01/01/2019'
AND
criteria2 > '0'
ORDER BY
criteria2
In below photo:
fieldz = Routing #
fieldX = Bank Account # (From table2)
fieldY = Bank Account # (From table1)
*This can be joined by student ID or Bank Account #, but the problem is the same regardless because both Student ID & Bank Account # appear multiple times in Table 1`
Table2 must have Table 1's key across multiple records. Needs to be 1:1 to achieve what you are describing.
With this query:
select t1.*, t2.routing
from table1 t1 left join table2 t2
on t2.studentid = t1.studentid
or this:
select t1.*, t2.routing
from table1 t1 left join table2 t2
on t2.bankaccount = t1.bankaccount
you get these results:
> studentid | bankaccount | gpa | semester | routing
> --------: | ----------: | ---: | :------- | :------
> 1 | 123456 | | Fall | abc
> 1 | 123456 | | Spring | abc
> 1 | 123456 | | Summer | abc
> 2 | 456789 | | Fall | def
> 2 | 456789 | | Spring | def
> 2 | 456789 | | Summer | def
> 3 | 321654 | | Fall | ghi
> 3 | 321654 | | Spring | ghi
> 3 | 321654 | | Summer | ghi
You can check the demo.

Access Queries comparing two tables

I have two tables in Access, Table A and Table B:
Table MasterLockInsNew:
+----+-------+----------+
| ID | Value | Date |
+----+-------+----------+
| 1 | 123 | 12/02/13 |
| 2 | 1231 | 11/02/13 |
| 4 | 1265 | 16/02/13 |
+----+-------+----------+
Table InitialPolData:
+----+-------+----------+---+
| ID | Value | Date |Type
+----+-------+----------+---+
| 1 | 123 | 12/02/13 | x |
| 2 | 1231 | 11/02/13 | x |
| 3 | 1238 | 10/02/13 | y |
| 4 | 1265 | 16/02/13 | a |
| 7 | 7649 | 18/02/13 | z |
+----+-------+----------+---+
All I want are the rows from table B for IDs not contained in A. My current code looks like this:
SELECT Distinct InitialPolData.*
FROM InitialPolData
WHERE InitialPolData.ID NOT IN (SELECT Distinct InitialPolData.ID
from InitialPolData INNER JOIN
MasterLockInsNew
ON InitialPolData.ID=MasterLockInsNew.ID);
But whenever I run this in Access it crashes!! The tables are fairly large but I don't think this is the reason.
Can anyone help?
Thanks
or try a left outer join:
SELECT b.*
FROM InitialPolData b left outer join
MasterLockInsNew a on
b.id = a.id
where
a.id is null
Simple subquery will do.
select * from InitialPolData
where id not in (
select id from MasterLockInsNew
);
Try using NOT EXISTS:
SELECT Distinct i.*
FROM InitialPolData AS i
WHERE NOT EXISTS (SELECT 1
FROM MasterLockInsNew AS m
WHERE m.ID = i.ID)

Query for joining all info from three tables

I need to Join 3 tables in SQL Server. Those 3 tables have basically this schema:
Users Items UsersItems
+--------+ +--------+-------------+ +--------+--------+-------+
| UserID | | ItemID | Description | | UserID | ItemId | Value |
+--------+ +--------+-------------+ +--------+--------+-------+
| 1 | | 1 | desc1 | | 1 | 1 | 1 |
| 2 | | 2 | desc2 | | 1 | 2 | 2 |
| ... | | ... | desc3 | | 2 | 2 | 1 |
| n | | n | desc4 | | n | 1 | 1 |
+--------+ +--------+-------------+ +--------+--------+-------+
As you can see both Users and Items can grow indefinitely and UsersItems is used to express the relation between those two, also including a Value column.
I need a query to retrieve all users, and for each user I need all the items with it's corresponding Value.
If the relation doesn't exist in UsersItems then Null (or a default value) should be returned for that row's Value column.
The expected query result should be:
ResultSet
+--------+--------+-------+
| UserID | ItemID | Value |
+--------+--------+-------+
| 1 | 1 | 1 |
| 1 | 2 | 2 |
| 1 | n | NULL |
| 2 | 1 | NULL |
| 2 | 2 | 1 |
| 2 | n | NULL |
| n | 1 | 1 |
| n | n | NULL |
+--------+--------+-------+
Okay, since there are several answers that I think aren't correct, I'll post what I think the answer is:
SELECT Users.UserID,
Items.Description,
UsersItems.Value
FROM
Users
CROSS JOIN
Items
LEFT JOIN
UsersItems
ON
Users.UserID = UsersItems.UserID
AND
Items.ItemID = UsersItems.ItemID
I'm inferring from your comment about nulls that you want to see all Items againsts all Users, with the Value from the UsersItems table where it exists.
SELECT
Users.UserID,
Items.Description,
Items.Value
FROM Users LEFT OUTER JOIN UsersItems
ON Users.UserID = UsersItems.UserID
LEFT OUTER JOIN Items
ON UserItems.ItemID = Items.ItemID
You do it like this:
SELECT Users.UserID, Items.Description, Items.Value
FROM Users
LEFT OUTER JOIN UsersItems ON Users.UserID = UsersItems.UserID
LEFT OUTER JOIN Items ON UserItems.ItemID = Items.ItemID
For more info on left outer join read the following:
The result of a left outer join (or simply left join) for table A and
B always contains all records of the "left" table (A), even if the
join-condition does not find any matching record in the "right" table
(B). This means that if the ON clause matches 0 (zero) records in B,
the join will still return a row in the result—but with NULL in each
column from B. This means that a left outer join returns all the
values from the left table, plus matched values from the right table
(or NULL in case of no matching join predicate). If the right table
returns one row and the left table returns more than one matching row
for it, the values in the right table will be repeated for each
distinct row on the left table. From Oracle 9i onwards the LEFT OUTER
JOIN statement can be used as well as (+).