Joining to the same table in SQL - SQL Server 2008 - sql

I have the following table:
ID Type Description IDOfSystem
--------------------------------
1000 Company Company Item NULL
1010 System System Item NULL
1020 Company NULL 1010
I have System and Company Items. I need to write a select query that gets all the company items and system items UNLESS if a company item has a value in IDOfSystem I need to exclude that system item and get the description from the system item.
So, given the above table, the SQL select should return rows 1000, 1020 (with "System Item") as the description.
If 1020 didn't exist, I'd simply get 1000 and 1010.
I guess I can break this up into multiple queries and do a UNION. I tried to do a left outer join on the same table but couldn't get the description from the system row.
Any help?

SELECT ID, Type, Description
FROM MyTable AS A
WHERE IDOfSystem IS NULL AND NOT EXISTS (SELECT *
FROM MyTable AS B
WHERE B.IDOfSystem = A.ID)
UNION ALL
SELECT A.ID, A.Type, B.Description
FROM MyTable AS A INNER JOIN MyTable AS B ON A.IDOfSystem = B.ID
WHERE IDOfSystem IS NOT NULL
What I'm doing is first selecting all rows that don't have a referenced system, and aren't used as some other rows system.
Then I'm doing a union with another query that finds all rows with a referenced system, and joining in the system to grab it's description.

SELECT
Companies.ID
,Companies.Type
,COALESCE(Systems.Description, Companies.Description) as Description
FROM YourTable Companies
LEFT OUTER JOIN YourTable Systems on Systems.ID = Companies.IDOfSystem
WHERE NOT EXISTS
(
SELECT * FROM YourTable T3 WHERE T3.IDOfSystem = Companies.ID
)
Here it is running on SEDE.

This approach uses a self join to look up the corresponding system's description. A separate subquery filters out the referenced systems.
select yt1.Id
, yt1.Type
, coalesce(yt2.Description, yt1.Description) as Description
from YourTable yt1
left join
YourTable yt2
on yt1.type = 'Company'
and yt2.type = 'System'
and yt2.ID = yt1.IDOfSystem
where yt1.type in ('System', 'Company')
and not exists
(
select *
from YourTable yt3
where yt1.type = 'System'
and yt3.type = 'Company'
and yt1.ID = yt3.IDOfSystem
)
Working example at SE Data.

I'm sure there are better ways of doing this, but try:
SELECT A.Id, A.Type, ISNULL(A.Description,B.Description) Description, A.IDOfsystem
FROM YourTable A
LEFT JOIN YourTable B
ON A.IDOFSystem = B.ID
WHERE A.ID NOT IN (SELECT IDOfsystem FROM YourTable WHERE IDOfsystem IS NOT NULL)

Related

SQL join best matcher

I have table A:
I need to join (SQL) this table onto table B, where I use ProductName as a join, but I want the following order of priorities:
Country being selected as a single row if it has a value (With Standard being null)
Using the combination of Country and Standard
Using Standard by itself (If Country being null).
I have tried looking around a lot. I hope the problem statement is clear.
Table A:
|ProductName|Country|Standard|Reportable|
|ProductA|Null|Value1|Y|
|ProductA|Value2|Value1|N|
|ProductA|Value2|Null|N|
The above is just a subset of the data, but basically the country and standard determine if the output is reportable. Product A could have 1 line or 3, depending on the data required.
Table B:
|ProductName|Year|
|ProductA|2006|
So the final join for the above should be:
|ProductName|Year|Country|Standard|Reportable|
|ProductA|2006|Value2|Null|N|
perhaps something like this: this is pseudo code at this time but hopefully gets the general concepts across.
Does assume A, B tables product are inner join related and not outer join.
the 1st CTE sets your priority. CTE (Common Table Expression)
the 2nd cte assigns a row number based on your priorities.
then the final query filters for the first row number encountered.
Obviously this is untested as we have no sample data or structure to test with at this time.
WITH CTE AS (
SELECT A.*
, B.*
, CASE WHEN A.Attribute is not null and B.attribute is null then 1
WHEN A.Attribute is not null and B.attribute is not null then 2
WHEN A.Attribute is null and B.Attribute is not null then 3 end as priority
FROM A
INNER JOIN B
on A.PRODUCT = B.PRODUCT),
CTE2 as (
SELECT CTE.*
, rowNumber() over (Order by Priority desc) RN
FROM CTE)
SELECT *
FROM CTE2
WHERE RN = 1
Something like this, maybe?
SELECT s.*, p.*
FROM source_table s
OUTER APPLY ( SELECT p.*
FROM product_table p
WHERE p.product_name = s.product_name
AND ( ( p.country = s.country AND s.standard IS NULL )
OR ( p.country = s.country AND p.standard = s.standard )
OR ( s.country IS NULL AND p.standard = s.standard )
ORDER BY CASE
WHEN ( p.country = s.country AND s.standard IS NULL ) THEN 1
WHEN ( p.country = s.country AND p.standard = s.standard ) THEN 2
WHEN ( s.country IS NULL AND p.standard = s.standard ) THEN 3
ELSE 99
FETCH FIRST 1 ROW ONLY )
OUTER APPLY (instead of CROSS APPLY) keeps your source_table result even if there is no product match. That may not be your desired outcome. If not, switch to CROSS APPLY.
There are probably ways to shorten the conditions and the sort order using NVL(). But I think this is the clearest way to start.
Also, if this is always a product match using one of those three conditions, you can shorten the WHERE clause in the OUTER APPLY.

SQL Table Joining

I'm joining these three tables, but the same information gets displayed 3 times ... Any idea how to have only the unique rows to be displayed, as determined by unique shipment id's?
SELECT S.SHIPMENT_ID, S.CREATION_DATE, S.BUSINESS_ID, B.BUS_ID, S.SHIPMENT_STATUS, S.BUSINESS_NAME, S.SHIPMENT_MODES, S.CUSTOMER_NAME
FROM "SHIPMENT" S
INNER JOIN "BUSINESS" B ON S.BUSINESS_ID=B.BUS_ID
INNER JOIN "SHIPMENT_GROUP" SG ON S.SHIPMENT_ID=SG.SHIPMENT_ID
INNER JOIN "DATA_GROUP" DG ON DG.ID=SG.GROUP_ID
try select distinct
SELECT DISTINCT column1, column2, ...
FROM table_name;
w3schools
You are selecting rows from the first table only, so this suggests that you are using the joins for filtering.
If so, you can rewrite this with exists, which will avoid duplicates if there are multiple matches. Starting from your existing query, the logic would be:
select s.*
from shipment s
where
exists (
select 1
from business b
where b.bus_id = s.business_id
) and exists (
select 1
from shipment_group sg
inner join data_group dg on dg.id = sg.group_id
where sg.shipment_id = s.shipment_id
)

How can I exclude users who have only purchase with car?

There is this SQL(from Django) query:
SELECT "id", "name"
FROM "polls_client" INNER JOIN "polls_purchases"
ON ("id" = "client_id")
WHERE "polls_purchases"."product" IN (car, bike)
We need to select from query users who have purchase records only 'car'. I want to do this in one select to the database. How do I do this?
You can group by client and set the condition in the HAVING clause:
SELECT pc.id, pc.name
FROM polls_client pc INNER JOIN polls_purchases pp
ON pc.id = pp.client_id
GROUP BY pc.id, pc.name
HAVING SUM(CASE WHEN pp.product <> 'car' THEN 1 ELSE 0 END) = 0
We need to select from query users who have purchase records only 'car'.
The simplest, most efficient method should be not exists:
SELECT c.*
FROM "polls_client" c
WHERE NOT EXISTS (SELECT 1
FROM "polls_purchases" pp
WHERE c."id" = pp."client_id" AND pp."product" <> 'car'
);
In particular, this can take advantage of an index on polls_purchases(client_id, product).
I would also dissuade your from using double quotes for identifies. They only serve to clutter queries.

How to get the Sum from three tables? [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
How to get Sum from two tables?
I have three table first "products" second "items" third "sales"
The first and second tables have the same columns('code','quantity') but the third table has ('code','name') now I want sum quantity from first and second but want also want get name from third table which code is equal.
check my code
Select code, sum(qtd),name
from ( select a.code, a.qtd from product a
union all select b.code, b.qtd from items b
union all select c.name from sales c where b.code=c.code
)
group by code
first two giving me perfect values but third fiction giving error not showing also names.
Hum....
SELECT
sales.name,
(SELECT SUM(products.quantity) FROM products WHERE products.code = sales.code) as products.quantity,
(SELECT SUM(items.quantity) FROM items WHERE items.code = sales.code) as items_quantity,
FROM sales
When using UNION be sure that the columns you want to combine matches with each SELECT statement. The first two SELECT statement works fine because they have the same number of columns. The third one failed because it has only one column.
The following are basic rules for combining the result sets of two (or more) queries by using UNION:
The number and the order of the columns must be the same in all queries.
The data types must be compatible.
From your query, I think you want like this,
SELECT a.code, b.name, SUM(a.qtd) totalSUM
FROM product a
INNER JOIN sales b
ON a.code = b.code
GROUP BY a.code, b.name
UNION
SELECT a.code, b.name, SUM(a.qtd) totalSUM
FROM items a
INNER JOIN sales b
ON a.code = b.code
GROUP BY a.code, b.name
I think you want to do something like this, though it's not clear what the actual requirements are:
select code = t1.code ,
name = t2.name ,
qtd = t1.qtd
from ( select code = t.code ,
qtd = sum( t.qtd )
from ( select a.code, a.qtd from product a
union all select b.code, b.qtd from items b
) t
group by t.code
) t1
join sales t2 where t2.code = t1.code
Cheers!

How to return rows matched in a table without multiple EXISTS clauses?

I want to pull back results from one table that match ALL specified values where the specified values are in another table. I can do it like this:
SELECT * FROM Contacts
WHERE
EXISTS (SELECT 1 FROM dbo.ContactClassifications WHERE ContactID = Contacts.ID AND ClassificationID = '8C62E5DE-00FC-4994-8127-000B02E10DA5')
AND EXISTS (SELECT 1 FROM dbo.ContactClassifications WHERE ContactID = Contacts.ID AND ClassificationID = 'D2E90AA0-AC93-4406-AF93-0020009A34BA')
AND EXISTS etc...
However that falls over when I get up to about 40 EXISTS clauses. The error message is "The query processor ran out of internal resources and could not produce a query plan. This is a rare event and only expected for extremely complex queries or queries that reference a very large number of tables or partitions. Please simplify the query."
The gist of this is to
Select all contacts with any GUID from the IN statement
Use a DISTINCT COUNT to get a count for each contactid on matching GUID's
Use the HAVING to retain only those contacts that equal the amount of matching GUID's you've put into the IN statement
SQL Statement
SELECT *
FROM dbo.Contacts c
INNER JOIN (
SELECT c.ID
FROM dbo.Contacts c
INNER JOIN dbo.ContactClassifications cc ON c.ID = cc.ContactID
WHERE cc.ClassificationID IN ('..', '..', 38 other GUIDS)
GROUP BY
c.ID
HAVING COUNT(DISTINCT cc.ClassificationID) = 40
) cc ON cc.ID = c.ID
Test script at data.stackexchange
One solution is to demand that no classification exists without a matching contact. That's a double negation:
select *
from contacts c
where not exists
(
select *
from ContactClassifications cc
where not exists
(
select *
from ContactClassifications cc2
where cc2.ContactID = c.ID
and cc2.ClassificationID = cc.ClassificationID
)
)
This type of problem is known as relational division.
SELECT c.*
FROM Contacts c
INNER JOIN
(cc.ContactID, COUNT(DISTINCT cc.ClassificationID) as num_class
FROM ContactClassifications
WHERE ClassificationID IN (....)
GROUP BY cc.ContactID
) b ON c.ID = b.ContactID
WHERE b.num_class = [number of distinct values - how many different values you put in "IN"]
If you run SQLServer 2005 and higher, you can do pretty much the same with CROSS APPLY, supposedly more efficiently