I have two tables:
dbo.Order
PK_Order FK_Customer OrderDate Total
1 1 2020-01-20 150.00
2 1 2020-01-25 200.00
dbo.Customer:
PK_Customer Name Age
1 John Miller 25
2 Max Monroe 28
I would like to join these two tables BUT when a customer has more than one order, only the one with the most recent date should be joined. This would be the initial code to join the two:
SELECT *
FROM dbo.Customer as Customer
LEFT OUTER JOIN dbo.Order
ON Customer.PK_Customer = dbo.Order.FK_Customer
I have never worked with case conditions in queries. Could anybody give me a hint?
I like using TOP 1 WITH TIES for problems like this:
SELECT TOP 1 WITH TIES *
FROM dbo.Customer c
LEFT OUTER JOIN o
ON c.PK_Customer = o.FK_Customer
ORDER BY
ROW_NUMBER() OVER (PARTITION BY c.PK_Customer ORDER BY o.OrderDate DESC);
You can LEFT JOIN only record with the latest date:
--CREATE TABLE [Order]
--(
-- PK_Order int,
-- FK_Customer int,
-- OrderDate date,
-- Total decimal(10,2)
--)
--INSERT [Order] VALUES
--(1,1,'2020-01-20',150),
--(2,1,'2020-01-25',200)
--CREATE TABLE Customer
--(
-- PK_Customer int,
-- Name nvarchar(20),
-- Age int
--)
--INSERT [Customer] VALUES
--(1,'John Miller',25),
--(2,'Max Monroe',28)
SELECT *
FROM dbo.Customer C
LEFT OUTER JOIN dbo.[Order] O
ON C.PK_Customer = O.FK_Customer
AND OrderDate=(SELECT MAX(OrderDate) FROM [Order] WHERE [Order].FK_Customer=O.FK_Customer)
Note 1: Since there can be many orders in recent date, I preserve all.
Note 2: It's not a good idea to keep age - it must be updated every year. Keep date of birth.
A similar way to Tim's answer but the difference is that the Partition by is within orders table and joining on Row =1 for each customer.
select * from #Customer c
left join
(select ROW_NUMBER() over (partition by FK_Customer order by OrderDate desc) as order_NUM,
PK_Order,
FK_Customer,
OrderDate,
Total from #Order
) o on c.PK_Customer = o.FK_Customer and order_NUM = 1
order by c.PK_Customer, o.OrderDate desc
I would like to achieve the below but not sure how to go about it any query pointing in the right direction will be a great help.
Tables: I have three tables below#
Merchant(MerchantId, Name, Date),
MerchantCategory(MerchantId, CategoryId),
Category (CategoryId, Name)
How to return category name, Merchant count,Merchant name with max date
From the requirement I understand that there should be 1 row per category, that the number of merchants should be shown and that the name of the merchant with the most recent date should be shown.
I have prepared a query below that generates some sample data and provides the result intended as I understand it.
The way this works is that the merchant volume is calculated by joining the merchant category table on to the category table and then counting the merchant id's per category. The name is trickier and requires using outer apply that per category (per row) works out the top 1 name in the merchant table ordered by the max(date) desc
I hope this helps, any questions please let me know.
declare #Merchant table (
MerchantId int,
Name nvarchar(25),
Date Date
);
declare #MerchantCategory table (
MerchantId int,
CategoryId int
);
declare #Category table (
CategoryId int,
Name nvarchar(25)
);
insert into #Merchant (MerchantId, Name, Date)
values
(1, 'Lucy', '2019-01-05'),
(2, 'Dave', '2019-01-30'),
(3, 'Daniel' ,'2019-02-01');
insert into #MerchantCategory (MerchantId, CategoryId)
values
(1, 4),
(1, 5),
(2, 4),
(3, 5);
insert into #Category (CategoryId, Name)
values
(4, 'Cat1'),
(5, 'Cat2');
select c. Name, max(m.name) as MaxMerchantName, count(distinct mc2.merchantid) as Merchantvol from #Category c
left join #MerchantCategory mc2 on c.CategoryId=mc2.CategoryId
outer apply (select top 1 name, max(date) as date from #Merchant m inner join #MerchantCategory mc on m.MerchantId=mc.MerchantId where c.CategoryId=mc.CategoryId group by Name order by max(date) desc) m
group by c.Name;
I would have expected to see your efforts..
so am going against SO principles # the moment..
try this:
select c.Name as category_name, count(*) as Merchant_Count, m.Name as Merchant_Name, max(Date) from
Merchant m join MerchantCategory mc
on m.MerchantId = mc.MerchantId
join Category c
on mc.CategoryId = c.CategoryId
group by c.Name, m.Name
I want to select all records for customers whose first order is from 2015. I want any orders they placed after 2015 too, but I DON'T want the records for customers whose first order was in 2016. I am ultimately trying to find the percentage of people who order more than twice, but I want to exclude the customers who were new in 2016.
This doesn't work because 'mindate' is an invalid column name but I'm not sure why or how else to try it.
Select
od.CustomerID, OrderID, OrderDSC, OrderDTS
From
OrderDetail OD
Join
(Select
OrderID, Min(orderdts) as mindate
From
OrderDetail
Where
mindate Between '2015-1-1' and '2015-12-31'
Group By Orderid) b on od.OrderID = b.OrderID
Because execution phases - it's seqency how is qry evaluated and by engine. In where clause your mindate not yet exists.
You can change mindate by orderdts:
select OrderID, min(orderdts) as mindate
from OrderDetail
where orderdts between '2015-1-1' and '2015-12-31'
group by Orderid
Second option is to use having statement - it's evaluated after group by.
What I di was select the distinct CustomerIDs that fall in between your daterange and did a left join with the table so it filters out anyone that doesn't fall in between your daterange.
SELECT * FROM
(Select DISTINCT(CustomerID) as CustomerID
FROM OrderDetail WHERE OrderDTS between '2015-1-1' AND '2015-12-31') oIDs
LEFT JOIN
OrderDetail OD
ON oIDs.CustomerID = OD.CustomerID
Try using the EXISTS clause. It is basically a sub-query. Below is an example you should be able to adapt.
create table Test (Id int, aDate datetime)
insert Test values (1,'04/04/2014')
insert Test values (1,'05/05/2015')
insert Test values (1,'06/06/2016')
insert Test values (2,'04/30/2016')
insert Test values (3,'02/27/2014')
select t.* from Test t
where
aDate>='01/01/2015'
and exists(select * from Test x where x.Id=t.Id and x.aDate >='01/01/2015' and x.aDate<'01/01/2016')
I don't know the orderdts data type but if it is datetime orders on 2015-12-31 will not be included (unless the order date is 2015-12-31 00:00:00.000. Note how this will skip the first record:
DECLARE #orders TABLE (CustomerID INT, orderDate DATETIME);
INSERT #orders VALUES (1, '2015-12-31 00:00:01.000'), (1, '2015-12-30'), (2, '2015-01-04');
SELECT * FROM #orders WHERE orderDate BETWEEN '2015-01-01' AND '2015-12-31';
In this case you would want the WHERE clause filter to look like:
WHERE orderDate BETWEEN '2015-01-01 00:00:00.000' AND '2015-12-31 23:59:59.999';
Or
WHERE CAST(orderDate AS date) BETWEEN '2015-01-01' AND '2015-12-31';
(the first example will almost certainly perform better).
Now, using this sample data:
-- Sample data
CREATE TABLE #LIST (LISTName varchar(10) NOT NULL);
INSERT #LIST
SELECT TOP (100) LEFT(newid(), 8)
FROM sys.all_columns a, sys.all_columns b;
-- You will probably want LISTName to be indexed
CREATE NONCLUSTERED INDEX nc_LISTName ON #LIST(LISTName);
You can implement Paul's solution like this:
DECLARE #LIST_Param varchar(8) = 'No List';
SELECT LISTName
FROM
(
SELECT distinct LISTName
FROM #LIST
UNION ALL
SELECT 'No List'
WHERE (SELECT COUNT(DISTINCT LISTName) FROM #LIST) < 1000000
) Distinct_LISTName
WHERE (#LIST_Param = 'No List' or #LIST_Param = LISTName);
Alternatively you can do this:
DECLARE #LIST_Param varchar(8) = 'No List';
WITH x AS
(
SELECT LISTName, c = COUNT(*)
FROM #LIST
WHERE (#LIST_Param = 'No List' or #LIST_Param = LISTName)
GROUP BY LISTName
),
c AS (SELECT s = SUM(c) FROM x)
SELECT LISTName
FROM x CROSS JOIN c
WHERE s < 1000000;
I'm attempting to enable purchase price comparisons across 2 or more members based on the members most recent price paid based on the purchase date.
I have four tables: Member, Items, UOM and Fact
Member (membername varchar(50), memberkey int)
Items (itemname varchar(50), itemkey int)
UOM (uomname varchar(50), uomkey int)
Fact (memberkey int, itemkey int, uomkey int, purchaseamount decimal(18,2), quantity int, purchasedate date)
My UI allows selection of two or more members to allow comparison of prices per uom. My result set has to include items where at least two of the selected members have purchased that item and exclude all others.
I set my member list in a temp table by the following:
declare #MemberKeys as varchar(max)
set #MemberKeys = '702,1382,1389,1390,1391,1392,1393,1394,1395,1396,1397,1401,1402,1404,1405,1406,1516,1844';
create table #mk (memberName varchar(253), memberkey smallint)
insert into #mk (memberName, memberkey)
Select Rownbr + '.) ' + membername, memberkey from (
SELECT
cast(ROW_NUMBER() OVER(ORDER BY [MemberFacilityName] ASC) as varchar (10)) AS RowNbr
,k.value as memberkey
,m.memberName
FROM
Member m
INNER JOIN dbo.String_To_SmallInt_Table(#MemberKeys, ',') AS k
ON m.Memberkey = k.value
) X
Then I use the temp table to filter when querying the fact, uom and item tables.
select m.membername
,i.itemname
,u.uomname
,purchaseamount
,quantity
,purchaseamount/quantity as price
from Fact f
join #mk m
on m.memberkey = f.memberkey
join Item i
on i.itemkey = f.itemkey
join UOM u
on u.uomkey= f.uomkey
Now I need to do the following but need some guidance to accomplish it.
1.) filter out items that are not used by at least two of the select members.
2.) show only the most recent purchase price per member\item\uom based on the purchase date.
3.) order the result set to show member then item for easy comparison (similar to the simplified list below).
Member Item Price
mbr1 A 1.11
mbr2 A 1.12
mbr3 A 1.52
mbr4 A 2.01
mbr1 B 3.01
mbr2 B 3.03
mbr3 B 3.12
mbr4 B 3.41
mbr1 C 6.01
mbr2 C 6.63
mbr3 C 6.92
mbr4 C 6.99
Here's how I implemented this...tell me if my logic is sound:
/****Create Sample Data*****/
-->Member table
IF exists (SELECT 1 from dbo.sysobjects WHERE name = 'Member')
DROP TABLE Member
GO
CREATE TABLE Member (membername VARCHAR(50), memberkey INT)
GO
INSERT INTO Member VALUES
('mbr1',702),
('mbr2',1382),
('mbr3',1389),
('mbr4',1390),
('mbr5',1391),
('mbr6',1392),
('mbr7',1393),
('mbr8',1394),
('mbr9',1395),
('mbr10',1396),
('mbr11',1397),
('mbr12',1401),
('mbr13',1402),
('mbr14',1404),
('mbr15',1405),
('mbr16',1406),
('mbr17',1516),
('mbr18',1111)-->Should NOT show up in query
GO
-->Items table
IF exists (SELECT 1 from dbo.sysobjects WHERE name = 'Items')
DROP TABLE Items
GO
CREATE TABLE Items (itemname VARCHAR(50), itemkey INT)
GO
INSERT INTO Items VALUES
('A',1),
('B',2),
('C',3),
('D',4)
GO
-->UOM table
IF exists (SELECT 1 from dbo.sysobjects WHERE name = 'UOM')
DROP TABLE UOM
GO
CREATE TABLE UOM (uomname VARCHAR(50), uomkey INT)
GO
INSERT INTO UOM VALUES ('QTY', 1)
GO
-->Fact table
IF exists (SELECT 1 from dbo.sysobjects WHERE name = 'Fact')
DROP TABLE Fact
GO
CREATE TABLE Fact (memberkey INT, itemkey INT, uomkey INT, purchaseamount decimal(18,2), quantity INT, purchasedate date)
GO
INSERT INTO Fact VALUES
(702, 1, 1, 1.11, 2, '1/3/2012'),-->Should show up in query
(1382, 1, 1, 1.12, 3, '1/4/2013'),-->Should NOT show up in query
(1382, 1, 1, 1.14, 2, '7/5/2013'),-->Should show up in query
(1404, 1, 1, 1.21, 2, '1/7/2012'),-->Should show up in query
(1401, 2, 1, 3.01, 1, '4/2/2013'),-->Should NOT show up in query
(1111, 3, 1, 6.92, 1, '12/12/2012'),-->Should NOT show up in query
(702, 3, 1, 5.01, 2, '4/1/2011'),-->Should show up in query
(1401, 3, 1, 4.01, 1, '6/5/2012'),-->Should show up in query
(1397, 4, 1, 5.45, 1, '7/4/2013'),-->Should NOT show up in query
(1397, 4, 1, 5.22, 3, '3/15/2011')-->Should NOT show up in query
GO
/*****Code to get results*****/
BEGIN
-->Members to Filter On
DECLARE #MemberKeys AS VARCHAR(max)
SET #MemberKeys = '702,1382,1389,1390,1391,1392,1393,1394,1395,1396,1397,1401,1402,1404,1405,1406,1516,1844';
-->Parse out comma delimited VALUES into a table variable
DECLARE #Member TABLE
(
memberkey INT
)
DECLARE #spot SMALLINT, #str VARCHAR(max), #sql VARCHAR(max)
WHILE #MemberKeys <> ''
BEGIN
SET #spot = CHARINDEX(',', #MemberKeys)
IF #spot>0
BEGIN
SET #str = LEFT(#MemberKeys, #spot-1)
SET #MemberKeys = RIGHT(#MemberKeys, LEN(#MemberKeys)-#spot)
END
ELSE
BEGIN
SET #str = #MemberKeys
SET #MemberKeys = ''
END
INSERT INTO #Member VALUES(CONVERT(VARCHAR(100),#str))
END
END;
-->Display Results
WITH staged(memberkey, membername, itemname ,itemkey, uomname, uomkey, purchaseamount, quantity, price, purchasedate, noitems )
AS
(
SELECT
m.memberkey
,m.membername
,i.itemname
,i.itemkey
,u.uomname
,u.uomkey
,f.purchaseamount
,f.quantity
,f.purchaseamount/f.quantity as price
,f.purchasedate
,COUNT(m.memberkey) OVER(PARTITION BY i.itemkey )-COUNT(m.memberkey) OVER(PARTITION BY convert(VARCHAR,m.memberkey)+convert(VARCHAR,i.itemkey) ) as noitems
FROM
Fact f
JOIN Member m ON m.memberkey = f.memberkey
JOIN Items i ON i.itemkey = f.itemkey
JOIN UOM u ON u.uomkey= f.uomkey
WHERE
EXISTS(SELECT 1 FROM #Member m2 WHERE m.memberkey=m2.memberkey)
)
SELECT
memberkey,
membername,
itemname ,
itemkey,
uomname,
uomkey,
sum(purchaseamount) as purchaseamount ,
sum(quantity) as quantity ,
sum(price) as price,
max(purchasedate) as purchasedate
FROM
staged st
WHERE
noitems>0
and exists(
select memberkey,
itemkey ,
uomkey,
max(purchasedate) as maxdate
from staged st2
where st.memberkey=st2.memberkey
and st.itemkey=st2.itemkey
and st.uomkey=st2.uomkey
group by
memberkey,
itemkey ,
uomkey
having st.purchasedate=max(st2.purchasedate)
)
GROUP BY
memberkey
,membername
,itemname
,uomname
, itemkey
, uomkey
ORDER BY
itemname
,memberkey;
I was able to figure this out on my own but will post my own answer; maybe it could help others with similar tasks.
I was able to complete the three tasks by introducing a second temp table to identify the most recent purchase price per item and member. Then joining the #mostrecentpurchase temp table to the base table enable effective member price comparisons.
To limit the result set to only items where two or more of the selected members had documented prices, I used the OVER clause and partitioned by item and unit of measure to get a count of members per item/uom. I then used this count in the where clause to filter out rows where the count was less than one.
Finally, the sorting was accomplished by simple order by clause. The completed tsql script is below.
declare #MemberKeys as varchar(max)
set #MemberKeys = '702,1382,1389,1390,1391,1392,1393,1394,1395,1396,1397,1401,1402,1404,1405,1406,1516,1844';
create table #mk (memberName varchar(253), memberkey smallint)
insert into #mk (memberName, memberkey)
Select Rownbr + '.) ' + membername, memberkey from (
SELECT
cast(ROW_NUMBER() OVER(ORDER BY [MemberFacilityName] ASC) as varchar (10)) AS RowNbr
,k.value as memberkey
,m.memberName
FROM
Member m
INNER JOIN dbo.String_To_SmallInt_Table(#MemberKeys, ',') AS k
ON m.Memberkey = k.value
) X
create table #mostrecentpurchase(purchasedate date, itemkey int, uomkey int, memberkey int)
Insert into #mostrecentpurchase(purchasedate, itemkey, uomkey, memberkey)
select max(f.PurchaseDate) purchasedate
, f.itemKey
, f.uomkey
, f.memberkey
from Fact f
join #mk m
on m.memberkey = f.memberkey
group by f.itemkey
, f.uomkey
, f.memberkey
select x.* FROM (
select m.memberName
, i.itemname
, i.itemkey
, f.purchasedate
, sum(f.purchaseamount) as purchaseamount
, sum(f.quantity) as quantity
, u.uomname
, sum(f.purchaseamount)/sum(f.quantity) as price
, count(m.memberName) OVER(PARTITION BY i.vendorItem_PK,u.UnitOfmeasure) AS mbrCount
from
fact f
join #mk m
on m.memberkey = f.memberkey
join #mostrecentpurchase mrp
on mrp.purchasedate = f.PurchaseDate
and mrp.memberkey = f.memberkey
and mrp.uomkey = f.uomkey
and mrp.vendoritemkey = f.itemkey
join item i
on i.itemkey = f.itemkey
join uom u
on u.uomkey = f.uomkey
group by m.membername,i.itemname,i.itemkey,f.purchasedate,u.uomname
) X
where mbrCount >= #MemberCompCount
order by X.itemname, X.memberName
drop table #mk;
drop table #mostrecentpurchase;