Performing a conditional join in SQL Server - sql-server

I have a SQL Server database. My database has two tables:
Customer Order
-------- -----
ID (int) ID (int)
Name CustomerID (int)
EmailAddress
When I query these tables, I might have a orderID. If I have an order ID, I want to return the customer associated with it. If I do NOT have an order ID, or if it equals 0, I want to return all of the customers. In an attempt to do this, I've written the following query:
SELECT
o.[ID]
FROM
[Order] o
WHERE
o.[ID]=#orderID
This query returns all orders with OrderID. However, I'm not sure how to do my conditional query. Is that even possible in SQL Server? If so, how?

For what you want you could use a case statement with Conduit's answer. Basically
CASE
WHEN #orderid = 0 OR #orderid IS NULL
SELECT * from Customer
ELSE
select c.*
from Customer c
inner join order o
on c.ID = o.CustomerID
where o.orderID = #orderID
END;
It may not be exact, I am not on a box with Sql Server installed.

I can think of a few ways to do this:
SELECT *
FROM Customer
WHERE CustomerID = coalsece( (select TOP 1 customerID from Orders WHERE OrderId= #OrderID), CustomerID)
.
With CustomerOrders As (
SELECT CustomerID, ID as OrderID
FROM Orders
WHERE OrderID = #OrderID
)
SELECT DISTINCT c.*
FROM Customer c
INNER JOIN CustomerOrders co ON c.ID = coalesce(co.CustomerID, c.ID)

You can achieve it using COALESCE in sql server -
SELECT c.*
FROM customer c
WHERE c.Id IN (
SELECT o.[CustomerID]
FROM [Order] o
WHERE o.[ID] = COALESCE(#orderID, o.[ID])
)

Related

Difference between Join and Subquery in SQL Server 2017

This is my first query:
select
r.[ProductID], [Name],
[Color], [UnitPrice]
from
[Production].[Product] r
inner join
[Sales].[SalesOrderDetail] d on d.productid = r.productid
This is my second query:
select [ProductID], [Name], [color]
from [Production].[Product] o
where [ProductID] in (select [UnitPrice]
from [Sales].[SalesOrderDetail] s
where s.ProductID = o.ProductID)
The first query returns correctly what I want to achieve, but the second query returns null. I want to use the second query to have the same result. Is that possible?
You can use exists :
select p.*
from product p
where exists (select 1 from SalesOrderDetail sd where sd.productid = p.product);
Your second query is not same as first query. It is comparing ProductID against UnitPrice which is invalid. Should produce no result.
For original version of query you can just change the sub query unit price with Productid :
where [ProductID] in (select sd.productid from SalesOrderDetail sd)
However, if you want unitprice (which is available in SalesOrderDetail) then this subquery will not help you. You need JOIN instead.

How to select multiple columns from a detail table in SQL Server

I have the following tables:
MASTER table (ID(PK), NAME, etc)
DETAIL table (ID(PK), IDMASTER(FK), VALUE1, DATE1, etc)
What I need is a SQL query or a way to do a select like
I work with SQL Server.
What I need is a SQL query or a way to do a select like
SELECT
M.ID, M.NAME,
(SELECT TOP 1 DT.ID, DT.VALUE1
FROM DETAIL D
WHERE D.IDMASTER = M.ID
ORDER BY DATE 1 DESC)
-- more than one column with a where clause and an order clause
FROM
MASTER M
Use OUTER APPLY:
SELECT M.ID,
M.NAME
D.ID,
D.VALUE1
FROM dbo.[MASTER] M
OUTER APPLY(SELECT TOP 1 ID, VALUE1
FROM dbo.DETAIL
WHERE IDMASTER = M.ID
ORDER BY [DATE] DESC) D;
You do this with APPLY operator:
select * from master m
outer apply(select top 1 * from detail d where d.masterid = m.id order by d.date1 desc)oa

Return only 1 address for each customer

I need to query the addresses of top 500 customers (best buyers). Many companies have multiple addresses.
The tables with data:
CustomerInfo
CustomerAddress
TransactionInfo
TransactionElements
My query looks this way:
select Customername, CustomerStreet --etc
from CustomerInfo
join CustomerAddress on CustomerID = Add_CustID
join TransactionInfo on Trn_CustID = CustomerID
JOIN TransactionElements ON Trn_CustID = TrE_CustID
GROUP BY CustomerName, CustomerStreet --etc
ORDER BY SUM (TrE_TranValue) DESC
It returns multiple addresses of a single company, I need just one.
One approach would be to use a CTE (Common Table Expression) if you're on SQL Server 2005 and newer (you aren't specific enough in that regard).
With this CTE, you can partition your data by some criteria - i.e. your CustomerId - and have SQL Server number all your rows starting at 1 for each of those "partitions", ordered by some criteria.
So try something like this:
;WITH CustomerAndAddress AS
(
SELECT
c.Customername, ca.CustomerStreet ,
ROW_NUMBER() OVER(PARTITION BY c.CustomerId ORDER BY ca.AddressID DESC) AS 'RowNum'
FROM
dbo.CustomerInfo c
INNER JOIN
dbo.CustomerAddress ca ON c.CustomerID = ca.Add_CustID
WHERE
......
)
SELECT
Customername, CustomerStreet
FROM
CustomerAndAddress
WHERE
RowNum = 1
Here, I am selecting only the "first" entry for each "partition" (i.e. for each CustomerId) - ordered by some criteria (I just arbitrarily picked AddressID from the address - adapt as needed) you need to define in your CTE.
Does that approach what you're looking for??
Something like this will work from sqlserver 2005+. I also suggest adding some aliasses to your tables and refer to those.
select CI.Customername, CA.CustomerStreet --etc
from CustomerInfo CI
cross apply
(select top 1 Customername, CustomerStreet --etc
from CustomerAddress where CustomerID = CI.Add_CustID) CA
join TransactionInfo TI on TI.Trn_CustID = CI.CustomerID
JOIN TransactionElements ON CI.CustomerID = TE.TrE_CustID
GROUP BY CustomerName, CustomerStreet --etc
ORDER BY SUM (TrE_TranValue) DESC
If CustomerInfo has many CustomerAddress, this query will make CustomerInfo returned for each CustomerAddress (a cartesian product) :
join CustomerAddress on CustomerID = Add_CustID
So if you need to get only one address you have to add conditions needed to choose single CustomerAddress :
join CustomerAddress on CustomerID = Add_CustID where <conditions>

SQL JOIN WITH WHERE

I am trying to join a table variable table and another table.
#ProcessTbl OrderDetail
Order ID
Status Order
Approved DateTime
Domain Status
OrderDetail table has multiple columns for same order. For example
ID Order DateTime Status
1 1 11-17-10 Recived
2 1 11-18-10 Processing
3 1 11-19-10 shipped
so what i want the join to do is take the order number from #processtbl(table variable)
and for max(id) get the datetime in this case the max id is 3
so my result should be
order status approved domain datetime
1 shipped true finance 11-19-10
SELECT t.order
,[od.DateTime]
,t.Status
,t.Domain
,t.Approved
FROM #ProcessTable t
JOIN OrderDetail od ON od.order= t.order
WHERE od.ID = (SELECT MAX(id) FROM orderdetail WHERE od.order = t.order )
ORDER BY od.[DateTime], Approved ASC
But I am still getting duplicate records , looks like it is joning both the tables.
How can I get distinct records?
select p.order, od.status, p.approved, p.domain, od.datetime
from #ProcessTable p
inner join (
select Order, max(ID) as MaxID
from OrderDetail
group by Order
) odm
inner join OrderDetail od on odm.Order = od.Order
and odm.MaxID = od.ID
Just a thought, have you tried to perform a SELECT DISTINCT?
WHERE od.ID = (SELECT MAX(id) FROM orderdetail WHERE od.order = t.order )
The od.order is not referencing the orderdetail table in the sub query but the instance outside.
Try something like: WHERE od.ID = (SELECT MAX(id) FROM orderdetail as od1 WHERE od1.order = t.order )

SQL Server 2005 Syntax Help - "Select Info based upon Max Value of Sub Query"

The objective is below the list of tables.
Tables:
Table: Job
JobID
CustomerID
Value
Year
Table: Customer
CustomerID
CustName
Table: Invoice
SaleAmount
CustomerID
The Objective
Part 1: (easy) I need to select all invoice records and sort by Customer (To place nice w/ Crystal Reports)
Select * from Invoice as A inner join Customer as B on A.CustomerID = B.CustomerID
Part 2: (hard) Now, we need to add two fields:
JobID associated with that customer's job that has the Maximum Value (from 2008)
Value associated with that job
Pseudo Code
Select * from
Invoice as A
inner join Customer as B on A.CustomerID = B.CustomerID
inner join
(select JobID, Value from Jobs where Job:JobID has the highest value out of all of THIS customer's jobs from 2008)
General Thoughts
This is fairly easy to do If I am only dealing with one specific customer:
select max(JobId), max(Value) as MaxJobID from Jobs where Value = (select max(Value) from Jobs where CustomerID = #SpecificCustID and Year = '2008') and CustomerID = SpecificCustID and CustomerID = '2008'
This subquery determines the max Value for this customer in 2008, and then its a matter of choosing a single job (can't have dupes) out of potential multiple jobs from 2008 for that customer that have the same value.
The Difficulty
What happens when we don't have a specific customer ID to compare against? If my goal is to select ALL invoice records and sort by customer, then this subquery needs access to which customer it is currently dealing with. I suppose this can "sort of" be done through the ON clause of the JOIN, but that doesn't really seem to work because the sub-sub query has no access to that.
I'm clearly over my head. Any thoughts?
How about using a CTE. Obviously, I can't test, but here is the idea. You need to replace col1, col2, ..., coln with the stuff you want to select.
Inv( col1, col2, ... coln)
AS
(
SELECT col1, col2, ... coln,
ROW_NUMBER() OVER (PARTITION BY A.CustomerID
ORDER BY A.Value DESC) AS [RowNumber]
FROM Invoice A INNER JOIN Customer B ON A.CustomerID = B.CustomerID
WHERE A.CustomerID = #CustomerID
AND A.Year = #Year
)
SELECT * FROM Inv WHERE RowNumber = 1
If you don't have a CustomerID, this will return the top value for each customer (that will hurt on performance tho).
The row_number() function can give you what you need:
Select A.*, B.*, C.JobID, C.Value
from
Invoice as A
inner join Customer as B on A.CustomerID = B.CustomerID
inner join (
select JobID, Value, CustomerID,
ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY Value DESC) AS Ordinal
from Jobs
WHERE Year = 2008
) AS C ON (A.CustomerID = C.customerID AND C.Ordinal = 1)
The ROW_NUMBER() function in this query will order by value in descending order and the PARTITION BY clause will do this separately for each different value of CustomerID. This means that the highest Value for each customer will always be 1, so we can join to that value.
The over function is an awesome, but often neglected function. You can use it in a subquery to pull back your valid jobs, like so:
select
a.*
from
invoice a
inner join customer b on
a.customerid = b.customerid
inner join (select customerid, max(jobid) as jobid, maxVal from
(select customerid,
jobid,
value,
max(value) over (partition by customerid) as maxVal
from jobs
where Year = '2008') s
where s.value = s.maxVal
group by customerid, maxVal) c on
b.customerid = c.customerid
and a.jobid = c.jobid
Essentially, that first inner query looks like this:
select
customerid,
jobid,
value,
max(value) over (partition by customerid) as maxVal
from jobs
where Year = '2008'
You'll see that this pulls back all of the jobs, but with that additional column which lets you know what the maximum value is for each customer. With the next subquery, we filter out any rows that have value and maxVal equal. Additionally, it finds the max JobID based on customerid and maxVal, because we need to pull back one and only one JobID (as per the requirements).
Now, you have a complete listing of CustomerID and JobID that meet the conditions of having the highest JobID that contains the maximum Value for that CustomerID in a given year. All that's left is to join it to Invoice and Customer, and you're good to go.
Just to be complete with the non row_number solution for those < MSSQL 2005. Personanly, I find it easier to follow myslef...but that could be biased considering how much time I spend in MSSQL 2000 vs 2005+.
SELECT *
FROM Invoice as A
INNER JOIN Customer as B ON
A.CustomerID = B.CustomerID
INNER JOIN (
SELECT
CustomerId,
--MAX in case dupe Values.
==If UC on CustomerId, Value (or CustomerId, Year, Value) then not needed
MAX(JobId) as JobId
FROM Jobs
JOIN (
SELECT
CustomerId,
MAX(Value) as MaxValue
FROM Jobs
WHERE Year = 2008
GROUP BY
CustomerId
) as MaxValue ON
Jobs.CustomerId = MaxValue.CustomerId
AND Jobs.Value = MaxValue.MaxValue
WHERE Year = 2008
GROUP BY
CustomerId
) as C ON
B.CustomerID = C.CustomerID

Resources