I have a SQL Server 2008 database. This database has two tables called Customer and Order. These tables are defined as follows:
Customer
--------
ID,
First Name,
Last Name
Order
-----
ID,
CustomerID,
Date,
Description
I am trying to write a query that returns all of the customers in my database. If the user has placed at least one order, I want to return the information associated with the most recent order placed. Currently, I have the following:
SELECT
*
FROM
Customer c LEFT OUTER JOIN Order o ON c.[ID]=o.[CustomerID]
As you can imagine, this will return all of the orders associated with a customer. In reality though, I only want the most recent one. How do I do this in SQL?
Thank you!
Here's a method that doesn't assume that the order dates are unique:
SELECT
Customer.ID CustomerID,
Customer.FirstName,
Customer.LastName,
T1.ID OrderID,
T1.Date OrderDate,
T1.Description OrderDescription
FROM Customer
LEFT JOIN (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY CustomerId ORDER BY Date DESC) AS rn
FROM [Order]
) T1
ON Customer.ID = T1.CustomerID AND T1.rn = 1
Result:
CustomerID FirstName LastName OrderID OrderDate OrderDescription
1 FirstName1 LastName1 2 2010-05-02 Description2
2 FirstName2 LastName2 3 2010-05-03 Description3
3 FirstName3 LastName3 NULL NULL NULL
Test data:
CREATE TABLE Customer (ID INT NOT NULL, FirstName VARCHAR(100) NOT NULL, LastName VARCHAR(100) NOT NULL);
INSERT INTO Customer (ID, FirstName, LastName) VALUES
(1, 'FirstName1', 'LastName1'),
(2, 'FirstName2', 'LastName2'),
(3, 'FirstName3', 'LastName3');
CREATE TABLE [Order] (ID INT NOT NULL, CustomerID INT NOT NULL, Date DATE NOT NULL, Description NVARCHAR(100) NOT NULL);
INSERT INTO [Order] (ID, CustomerID, Date, Description) VALUES
(1, 1, '2010-05-01', 'Description1'),
(2, 1, '2010-05-02', 'Description2'),
(3, 2, '2010-05-03', 'Description3'),
(4, 2, '2010-05-03', 'Description4');
select c.ID, c.FirstName, c.LastName, o.ID as OrderID, o.Date, o.Description
from Customer c
left outer join (
select CustomerID, max(Date) as MaxDate
from Order
group by CustomerID
) om on c.ID = om.CustomerID
left outer join Order o on om.CustomerID = o.CustomerID and om.MaxDate = o.Date
I would use where clause with Max() function to guarantee the latest added record:
(you code...)
Where o.id = max(o.id)
Select * from
Customer C Left join
(
Select o.CustomerID, Description, Date from
Orders o inner join
(
Select CustomerID, Max(Date) as LastOrder
From Orders Group by CustomerID
) SubLatest on o.CustomerID = SubLatest.CustomerID
and o.Date = SubLatest.LastOrder
) SubDetails
on C.id = SubDetails.CustomerID
Not Homework I hope! :O)
Select Top 1 C.*
,O.*
From Customer C left outer join
Order O on O.CustomerId = C.Id
Order by O.[Date] Desc
Hope that helps
Related
I have 3 tables in SQL Server:
Sales (customerId)
Customer (customerId, personId)
Person (personId, firstName, lastName)
and I need to return the top 10 customers.
I used this query:
SELECT TOP 10
CustomerID, COUNT(CustomerID)
FROM
Sales
GROUP BY
(CustomerID)
ORDER BY
COUNT(CustomerID) DESC
The query currently returns only the customerId and count, but I also need to return the firstName and lastName of these customers from the Person table.
I know I need to reach the firstName and lastName by correlating between Sales.customerId and Customer.customerId, and from Customer.personId to get the Person.personId.
My question is whether I need to use an inner join or union, and how to use either of them to get the firstName and lastName of these customers
Union is mostly used for disjoint sets. To achieve your target, u can go with inner-join.
If you want to use joins, then here is the query which works similarly to your requirement.
SELECT TOP 10 S.CustomerID, P.FirstName,P.LastName, count(*)
FROM Sales S
INNER JOIN Customer C on S.CustomerId=C.CustomerId
INNER JOIN Person P on C.PersonId = P.PersonId
GROUP BY (S.CustomerID, P.FirstName,P.LastName)
ORDER BY count(*) DESC
You need use inner join like this :
SELECT TOP 10 S.CustomerID
, P.FirstName
, P.LastName
, COUNT (1) AS CountOfCustomer -- this is equal count(*)
FROM Sales S
INNER JOIN Customer C ON S.CustomerId = C.CustomerId
INNER JOIN Person P ON C.PersonId = P.PersonId
GROUP BY S.CustomerID, P.FirstName, P.LastName
ORDER BY 4 DESC; -- this is equal order by count(*)
I have two tables in SQL Server GRV and GIV with these columns:
GRV : Date, ProductID, ProductName, Unit, ReceivedQTY
GIV : Date, ProductID, ProductName, Unit, Quantity
Query is as follows:
select
GRV.ProductID, GRV.ProductName, GRV.Unit, GRV.ReceivedQTY,
GIV.ProductID, GIV.ProductName, GIV.Unit, GIV.Quantity
from
GRV
full outer join
GIV on GRV.ProductID = GIV.ProductID
This is what I am getting:
The problem is the rows of red font are not actually in my GIV table. What I want is only actual data of table should combine as it is. GRV on right side and GIV on left side without even rows indicating null.
Is there any option available? The reason I need this to create a stock ledger Crystal Report where I can show all transactions of received and issue quantity date wise and generate closing balance in the end. Please help me in this regard.
Your question is missing a clear "expected result".
Here are some options that might help you.
Sample data
create table ItemIssued
(
ProductId nvarchar(5),
Quantity int
);
insert into ItemIssued (ProductId, Quantity) values
('P0001', 100),
('P0002', 50),
('P0004', 1);
create table ItemReceived
(
ProductId nvarchar(5),
Quantity int
);
insert into ItemReceived (ProductId, Quantity) values
('P0002', 55),
('P0003', 200);
Solution 1
With null.
select i.ProductId as ProductId,
i.Quantity as Quantity,
r.ProductId as ProductId,
r.Quantity as Quantity
from ItemIssued i
full join ItemReceived r
on r.ProductId = i.ProductId;
Solution 2
Without null.
select coalesce(i.ProductId,'') as ProductId,
coalesce(convert(nvarchar(5), i.Quantity),'') as Quantity,
coalesce(r.ProductId,'') as ProductId,
coalesce(convert(nvarchar(5), r.Quantity),'') as Quantity
from ItemIssued i
full join ItemReceived r
on r.ProductId = i.ProductId;
Solution 3
Tables next to each other.
with ctei as
(
select row_number() over(order by i.ProductId) as RowNum,
i.ProductId,
i.Quantity
from ItemIssued i
),
cter as
(
select row_number() over(order by r.ProductId) as RowNum,
r.ProductId,
r.Quantity
from ItemReceived r
)
select ctei.ProductId,
ctei.Quantity,
cter.ProductId,
cter.Quantity
from ctei
full join cter
on cter.RowNum = ctei.RowNum;
Solution 4
All products with quantities.
with cte as
(
select i.ProductId
from ItemIssued i
union
select r.ProductId
from ItemReceived r
)
select c.ProductId,
i.Quantity,
r.Quantity
from cte c
left join ItemIssued i
on i.ProductId = c.ProductId
left join ItemReceived r
on r.ProductId = c.ProductId;
Results
In the same order as the solutions.
Fiddle to see it in action.
In my calculated data layer, I am attempting to populate a Customer's postcode at the time of the order, a sub sample of the table being populated is as follows:
CustomerOrders
(
CustomerID varchar(20),
...
OrderDate date,
...
CustomerPostcodeAtTimeOfOrder varchar(10)
)
This table is a join of the Customers table, the Orders table and the CustomerAddress table which looks like follows:
CustomerAddress
(
CustomerID varchar(20),
AddressType varchar(10),
/*
AddressDetails
*/
StartDate date,
EndDate date,
AddressRank int
)
It is quite conceivable that a customer may have recorded addresses of various types for a single date so the intention when populating the CustomerOrders table is to join as below:
SELECT *
FROM Customers c
LEFT JOIN Orders o
ON o.CustomerID = c.CustomerID
OUTER APPLY
(
SELECT TOP 1 Postcode
FROM CustomerAddress ca
WHERE ca.CustomerID = c.CustomerID
AND o.OrderDate BETWEEN ca.StartDate AND ca.EndDate
ORDER BY AddressRank
)
However, the performance hit I am getting by adding this join to the query means that returning 1000 rows goes from taking 4 seconds to taking 106 seconds.
Just to note, I have added a non-clustered index on the Address table too. The definition of which is as below:
CREATE NONCLUSTERED INDEX (IX_CustomerAddress)
ON CustomerAddress (StartDate, EndDate)
INCLUDE (AddressRank, CustomerID, Postcode)
I'm looking for any suggestions on the best way to tackle this issue please?
I'm not completely sure if this will return results faster, but you can rewrite your query like this:
;WITH OrderAddress AS
(
SELECT o.*,
ca.Postcode,
RN = ROW_NUMBER() OVER(PARTITION BY CustomerID ORDER BY AddressRank DESC)
FROM CustomerAddress ca
INNER JOIN Orders o
ON ca.CustomerID = c.CustomerID
AND o.OrderDate BETWEEN ca.StartDate AND ca.EndDate
)
SELECT *
FROM Customers c
LEFT JOIN ( SELECT *
FROM OrderAddress
WHERE RN = 1) o
ON o.CustomerID = c.CustomerID;
You should also post the index definition on the Address table.
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])
)
I have a table with data and I am trying to find max date verified
Create table staging(ID varchar(5) not null, Name varchar(200) not null, dateverified datetime not null,dateinserted datetime not null)
ID,Name,DateVerified,DateInserted
42851775,384,2014-05-24 08:48:20.000,2014-05-28 14:28:10.000
42851775,384,2014-05-28 13:13:07.000,2014-05-28 14:36:12.000
42851775,a1d,2014-05-28 09:17:22.000,2014-05-28 14:36:12.000
42851775,a1d,2014-05-28 09:17:22.000,2014-05-28 14:28:10.000
42851775,a1d,2014-05-28 09:17:22.000,2014-05-28 14:29:08.000
42851775,bc5,2014-05-28 09:17:21.000,2014-05-28 14:29:08.000
42851775,bc5,2014-05-28 09:17:21.000,2014-05-28 14:28:10.000
42851775,bc5,2014-05-28 09:17:21.000,2014-05-28 14:36:12.000
I want to display max dateverified for each keyid i.e.
42851775,384,2014-05-28 13:13:07.000,2014-05-28 14:36:12.000
42851775,a1d,2014-05-28 09:17:22.000,2014-05-28 14:36:12.000
42851775,bc5,2014-05-28 09:17:21.000,2014-05-28 14:29:08.000
SELECT i.[ID],i.name,i.dateinserted,r.maxdate
FROM (select id,name,max(dateverified) as maxdate from
[dbo].[staging] where id=42851775 group by id,name) r
inner join
[dbo].[staging] i
on r.id=i.id and r.jobpostingurl=i.jobpostingurl and r.maxdate=i.dateverified
group by i.id,i.jobpostingurl,r.maxdate
I get an error,dateinserted is invalid as it is not contained in group by clause. But if I add it in group by clause I get all 8 records. How to handle this?
Thanks
R
SELECT
KeyID,
MAX(yourDate)
FROM
Staging
GROUP BY
KeyID
If you want additional information join this to another table for instance:
SELECT
b.KeyID,
a.dateinserted,
b.TheDate
FROM YourTable a
INNER JOIN
(
SELECT
KeyID,
MAX(yourDate) AS TheDate
FROM
Staging
GROUP BY
KeyID
) b
ON
b.KeyID = a.KeyID
If you need to get the dateinserted you can use a cte and join it back to the original table:
WITH cte
AS ( SELECT [ID] ,
name ,
MAX(dateverified) AS dateverified
FROM [dbo].[staging]
GROUP BY ID ,
name
)
SELECT cte.[ID] ,
cte.NAME ,
cte.dateverified ,
s.Dateinserted
FROM cte
INNER JOIN dbo.staging s ON cte.[ID] = s.[ID]
AND cte.NAME = s.NAME
AND cte.dateverified = s.dateverified