there are 2 tables:
people
+------------+--------------+------+
| name | place | pid |
+------------+--------------+------+
| Mr John | place1 | 1 |
| Miss Smith | place2 | 2 |
+------------+--------------+------+
places
+------+------+----------------------+
| pid | owner| address |
+------+------+----------------------+
| 1 | 1 | address1 |
| 1 | null | address2 |
| 2 | null | address3 |
| 2 | null | address4 |
| 2 | null | address5 |
+------+------+----------------------+
I am looking for a query which will return:
people (complex left join) places on people.pid = places.pid
Mr John | place1 | 1 | 1 | 1 | address1
Miss Smith | place2 | 2 | 2 | null | address3
Miss Smith | place2 | 2 | 2 | null | address4
Miss Smith | place2 | 2 | 2 | null | address5
In words a join on pid but if there is a non null owner value for the specific person then get only that row, if there is not a non null owner value then get all the rows for the specific person. Using left join because I need also the people with pid = null
One strategy is to pre process the places table in a CTE to identify which pid group of records have at least one non NULL owner value. Such pid records need to all be included in the join. On the other hand, if a pid group has at least one non NULL owner, then we will only include non NULL matches in the join.
WITH cte AS (
SELECT pid, owner, address,
SUM(CASE WHEN owner IS NOT NULL THEN 1 ELSE 0 END) OVER
(PARTITION BY pid) AS non_null_cnt
FROM places
)
SELECT t1.name, t1.place, t1.pid, t2.owner, t2.address
FROM people t1
LEFT JOIN cte t2
ON t1.pid = t2.pid AND
(t2.owner IS NOT NULL OR t2.non_null_cnt = 0)
ORDER BY t1.pid;
Demo
;WITH CTE as
(
SELECT count(owner)over(partition by pid) mo,*
FROM places
)
SELECT *
FROM people p
LEFT JOIN CTE
ON
p.pid = CTE.pid
and (mo = 0
or owner is not null)
Related
i would like to join 2 table but not duplicating all data.
try to be more clear
I have table_A
| ID | Description | Total
| 1. | Test a. | 10
| 2. | Test B. | 8
and my total is 18
Table_B
|ID| Site
|1 | Site A
|1 | Site B
|2 | Site C
If i do a left
Select a.ID,a.Description,b.Site,a.Total from table_a as a
left outer join table_b as b on a.id =b.it
i get
| ID | Description | Site | Total
| 1. | Test a. | Site A|. 10
| 1. | Test a. | Site B|. 10
| 2. | Test B. | Site C| 8
so my total became 28
i would like to get something like
| a.ID |b.ID| Description | Site | Total
| 1. | | Test a. | |. 10
| 1. | 1 | | Site A|
| 1. | 1 | | Site B|
| 2. | | Test B. | | 8
| 2. | 2 | | Site C|
so i can have it in excel and create a group by into the row
I think this is what you want. There are a few methods of doing this, one is using a UNION to get the 2 datasets:
SELECT aID,
bID,
description,
Site,
Total
FROM (SELECT a.ID AS aID,
b.ID AS bID,
a.description,
b.Site,
NULL AS Total
FROM dbo.TableA a
JOIN dbo.TableB b ON A.ID = B.ID
UNION ALL
SELECT a.ID AS aID,
NULL,
a.description,
NULL,
a.Total
FROM dbo.TableA a) U
ORDER BY aID,
bID,
Site;
db<>fiddle
Basically I have two tables. Customer and Purchase table. My Problem is the Purchase Table is very large and causing performance issues, and I'm trying to keep my code organized into relevant CTE's.
I'm trying to pull all the purchase records for those who purchased a Guitar Type A or have no purchases.
I want to filter out any customer who didn't buy a GuitarType A but still keep customer who didn't buy anything.
Here's my code:
WITH Purchases AS
(
SELECT
, CustID
, GuitarType
FROM
Purchase
WHERE
GuitarType = 'A'
)
,
RelevantCustomers AS
(
SELECT
P.Custid
, P.PurchaseDate
, C.CustType
FROM
Customer
)
SELECT
Custid
, GuitarType
, PurchaseDate
FROM
Purchases AS p
INNER JOIN
RelevantCustomers AS rc ON p.CustId= rc.CustId
Customer:
+--------+-------------+----------+
| CustId | CreatedDate | CustType |
+--------+-------------+----------+
| 1 | 01/01/2017 | A |
+--------+-------------+----------+
| 2 | 01/01/2018 | B |
+--------+-------------+----------+
| 4 | 01/01/2018 | C |
+--------+-------------+----------+
Purchase
+----------+--------------+------------+
| GuitarId | PurchaseDate | GuitarType |
+----------+--------------+------------+
| 1 | 04/01/2018 | A |
+----------+--------------+------------+
| 1 | 05/01/2018 | A |
+----------+--------------+------------+
| 1 | 06/01/2018 | C |
+----------+--------------+------------+
| 2 | 06/01/2018 | A |
+----------+--------------+------------+
| 2 | 06/01/2018 | B |
+----------+--------------+------------+
| 2 | 06/01/2018 | A |
+----------+--------------+------------+
If I use INNER JOIN then it will only return those who bought Guitar Type A. If I use LEFT then it will include all Customers.
One alternative is to move the "Where GuitarType = 'A' down to the where clause and do a LEFT JOIN but this will cause my code to be unorganized and potentially some performance issues.
This might do it
SELECT rc.Custid, p.GuitarType, p.PurchaseDate
FROM RelevantCustomers rc
LEFT JOIN Purchases p
ON p.CustId = rc.CustId
LEFT JOIN Purchases pn
ON pn.CustId = rc.CustId
AND p.GuitarType != 'A'
WHERE (p.GuitarType = 'A' OR p.CustID IS NULL)
and pn.CustID is null
You appear to want:
SELECT rc.Custid, p.GuitarType, p.PurchaseDate
FROM RelevantCustomers rc LEFT JOIN
Purchases p
ON p.CustId = rc.CustId
WHERE p.GuitarType = 'A' OR p.GuitarType IS NULL;
For performance, you want an index on Purchases(CustId).
Please, can someone help me with what is possibly a simple query?
We have two tables with below structure.
Customer table:
+----+-----------+
| id | name |
+----+-----------+
| 1 | customer1 |
| 2 | customer2 |
| 3 | customer3 |
+----+-----------+
Customer role mapping table:
+-------------+-----------------+
| customer_id | customerRole_id |
+-------------+-----------------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
+-------------+-----------------+
I want to select customers with role id 1 only NOT with role id 1 AND 2.
So, in this case, it would be customer id 2,3,4 & 5. ignoring 1 as it has multiple roles.
Is there a simple query to do this?
Many thanks, for any help offered.
Hmmm, there are several ways to do this.
select c.*
from customers c
where exists (select 1 from mapping m where m.customerid = c.id and m.role = 1) and
not exists (select 1 from mapping m where m.customerid = c.id and m.role <> 1);
If you just want the customer id, a perhaps simpler version is:
select customerid
from mapping
group by customerid
having min(role) = 1 and max(role) = 1;
This solution assumes that role is never NULL.
This query may seem basic, but i'm at a fairly basic level.
So here is my data - Sorry about the formatting, i've tried following the help but the table formatting is obviously not working for me (Can someone please advise?):
Table 1
ID |Country
---| -------
1 | UK
1 | IE
1 | US
2 | UK
2 | FR
Table 2
ID |Country
---| -------
1 | UK
1 | IE
2 | UK
The result i want is this
Table 1----- | ----Table 2
ID |Country |-----ID |Country
---| ------- |--------|--------
1 | UK | 1 | UK
1 | IE | 1 | IE
1 | US | 1 | NULL
2 | UK | 2 | UK
2 | FR | 2 | NULL
But more specifically i want to identify the NULL's so that i get this result
Table 1----- | ----Table 2
ID |Country |-----ID |Country
---| ------- |--------|--------
1 | US | 1 | NULL
2 | FR | 2 | NULL
The code i have used so far is:
select *
from table1 t1
left outer join table2 t2 on t1.id = t2.id and t1.country = t2.country
where t1.id is not null
and t2.country is null
Try this
select t1.id, t1.country, isnull(t2.id, t1.id) AS T2_ID, t2.country
from table1 t1
left outer join table2 t2 on t1.id = t2.upc and t1.country = t2.country
if you want to only show the ones where you have nulls in t2, you can add
where t2.id is null
But if you want to show all the records, just leave it without the WHERE condition
You were close, you just need to use isnull() or coalesce().
select
t1.id
, t1.country
, t2_Id = isnull(t2.id,t1.id)
, t2_country = t2.country
from table1 t1
left outer join table2 t2 on t1.id = t2.id and t1.country = t2.country
where t1.id is not null
--and t2.country is null
rextester demo: http://rextester.com/XCNH52338
returns:
+----+---------+-------+------------+
| id | country | t2_Id | t2_country |
+----+---------+-------+------------+
| 1 | UK | 1 | UK |
| 1 | IE | 1 | IE |
| 1 | US | 1 | NULL |
| 2 | UK | 2 | UK |
| 2 | FR | 2 | NULL |
+----+---------+-------+------------+
with the additional filter of t2.country is null
returns:
+----+---------+-------+------------+
| id | country | t2_Id | t2_country |
+----+---------+-------+------------+
| 1 | US | 1 | NULL |
| 2 | FR | 2 | NULL |
+----+---------+-------+------------+
The main difference between the two is that coalesce() can support more than 2 parameters, and it selects the first one that is not null. More differences between the two are answered here.
coalesce() is standard ANSI sql, so it is available in most RDBMS. isnull() is specific to sql server.
Reference:
isnull() - msdn
coalesce() - msdn
coalesce vs isnull - Itzik Ben-Gan
I'm relatively new to SQL and am running into a lot of issues trying to figure this one out. I've tried using a LEFT JOIN, and dabbled in using functions to get this to work but to no avail.
For every UserID, if there is a NULL value, I need to remove all records of the Product ID for that UserID from my SELECT.
I am using SQL Server 2014.
Example Table
+--------------+-------------+---------------+
| UserID | ProductID | DateTermed |
+--------------+-------------+---------------+
| 578 | 2 | 1/7/2017 |
| 578 | 2 | 1/7/2017 |
| 578 | 1 | 1/15/2017 |
| 578 | 1 | NULL |
| 649 | 1 | 1/9/2017 |
| 649 | 2 | 1/11/2017 |
+--------------+-------------+---------------+
Desired Output
+--------------+-------------+---------------+
| UserID | ProductID | DateTermed |
+--------------+-------------+---------------+
| 578 | 2 | 1/7/2017 |
| 578 | 2 | 1/7/2017 |
| 649 | 1 | 1/9/2017 |
| 649 | 2 | 1/11/2017 |
+--------------+-------------+---------------+
Try the following:
SELECT a.userid, a.productid, a.datetermed
FROM yourtable a
LEFT OUTER JOIN (SELECT userid, productid, datetermed FROM yourtable WHERE
datetermed is null) b
on a.userid = b.userid and a.productid = b.productid
WHERE b.userid is not null
This will left outer join all records with a null date to their corresponding UserID and ProductID records. If you only take records that don't have an associated UserID and ProductID in the joined table, you should only be left with records that don't have a null date.
You can use this WHERE condition:
SELECT
UserID,ProducID,DateTermed
FROM
[YourTableName]
WHERE
(CONVERT(VARCHAR,UserId)+
CONVERT(VARCHAR,ProductID) NOT IN (
select CONVERT(VARCHAR,UserId)+ CONVERT(VARCHAR,ProductID)
from
[YourTableName]
where DateTermed is null)
)
When you concatenate the UserId and the ProductId get a unique value for each pair, then you can use them as a "key" to exclude the "pairs" that have the null value in the DateTermed field.
Hope this help.