September 11, 2018 at 12:20 pm
Hi,
Is there another way of writing this query without first having to get the MAX date for an id and then get the record for that id?
SELECT t0.IdSeller, t0.ProductsCount, t0.AvgPrice FROM SellerProductsSnapshots t0
INNER JOIN (
SELECT IdSeller, MAX(Date) date FROM SellerProductsSnapshots
WHERE IdSeller IN (SELECT Id FROM #SellersTable)
GROUP BY IdSeller
) t1 ON t0.IdSeller = t1.IdSeller AND t1.date = t0.Date
I could do it with ROW_NUMBER and PARTITION BY id ORDER BY date DESC and get the first row but wouldn't that be the same?
Thanks,
Pedro
September 11, 2018 at 12:30 pm
This may get the same thing, without data I can't test:SELECT t0.IdSeller, t0.ProductsCount, t0.AvgPrice,
MAX(Date) AS Maxdate
FROM SellerProductsSnapshots t0
WHERE IdSeller IN (SELECT Id FROM #SellersTable)
GROUP BY IdSeller, t0.ProductsCount, t0.AvgPrice
I would avoid the ROW_Number option, only when no other way exists.
-------------------------------------------------------------
we travel not to escape life but for life not to escape us
Don't fear failure, fear regret.
September 11, 2018 at 12:52 pm
below86 - Tuesday, September 11, 2018 12:30 PMThis may get the same thing, without data I can't test:SELECT t0.IdSeller, t0.ProductsCount, t0.AvgPrice,
MAX(Date) AS Maxdate
FROM SellerProductsSnapshots t0
WHERE IdSeller IN (SELECT Id FROM #SellersTable)
GROUP BY IdSeller, t0.ProductsCount, t0.AvgPrice
I would avoid the ROW_Number option, only when no other way exists.
Duhhhh...
Thanks... Didn't see that one... 😀
September 11, 2018 at 1:20 pm
No problem, sometimes helps to have another set of eyes.
See if this would be any faster:SELECT t0.IdSeller, t0.ProductsCount, t0.AvgPrice,
MAX(Date) AS Maxdate
FROM SellerProductsSnapshots t0
INNER JOIN #SellersTable
ON IdSeller = Id
GROUP BY IdSeller, t0.ProductsCount, t0.AvgPrice
-------------------------------------------------------------
we travel not to escape life but for life not to escape us
Don't fear failure, fear regret.
September 11, 2018 at 7:45 pm
since you didn’t bother to follow forum rules. I guess we’ll just have to guess at the keys and everything else that you fail to post. You have some obvious problems. First of all, there can never be a data element named just “date†because a date is a unit of temporal measure, and never a data element. When you get a good book on data modeling, you’ll learn about attribute properties. This means that the date property has to belong to a particular attribute (hire_date, termination_date, birth_date, etc.) and since you failed to tell us, all guess that is the date of the sale.
Another principle is DRY, which stands for “don’t repeat yourselfâ€; this means that the table name should appear only once in a query. Using table aliases that give absolutely no clue as to what set of entities or what relationship the table models is pretty useless. In particular, putting a number mimics the way tape drives were named back in the 1960s on IBM systems (MT0, MT1, MT2, etc) Is this why you confused records (mag taps) with rows?
Mixing data and metadata in a data element name is a horrible design flaw. In fact, putting the word “_table†or some variation of it is so bad it has a name; it’s called a Tibble and we laugh at it.
We also don’t like the use of temporary tables. This is how non-SQL programmers fake their familiar scratch tapes in their new language.
>> I could do it with ROW_NUMBER and PARTITION BY id ORDER BY date DESC and get the first row but wouldn't that be the same? <<
Why do all that extra work? You only need the most recent sale date. Why would you want to ask for an extra sort instead of just the max?
WITH Recent_Sales
AS
(SELECT SP.seller_id, SP.product_cnt, SP.sale_price_avg,
SP.sale_date, MAX(sale_date) OVER() AS sale_date_max
FROM SellerProducts AS SP)
SELECT seller_id, product_cnt, sale_price_avg, sale_date
FROM Recent_Sales
WHERE sale_date = sale_date_max
AND seller_id IN ( ??? );
I’m not sure how to get that list of seller_id’s, but I do know the name of the column would not become “id†in one place in your schema and then suddenly become a “seller_id†somewhere else.
Please post DDL and follow ANSI/ISO standards when asking for help.
September 14, 2018 at 7:51 am
...and so, we have a perfect example of someone trying to help - and another person, not.
September 14, 2018 at 2:12 pm
In addition to the ROW_NUMBER method offered up by below86, here are two additional methods... One is usually faster than the others depending on existing indexes so all are worth testing.
-- Option 1
SELECT
IdSeller = st.Id,
spsx.ProductsCount
spsx.AvgPrice
FROM
#SellersTable st --(assuming the id is already unique)
CROSS APPLY (
SELECT TOP (1)
sps.ProductsCount,
sps.AvgPrice
FROM
dbo.SellerProductsSnapshots sps
ORDER BY
sps.Date DESC
WHERE
st.id = sps.IdSeller
) spsx;
-- Option 2
SELECT
IdSeller = st.Id,
ProductsCount = CONVERT(INT, SUBSTRING(spsx.max_bin_val, 9, 4)),
AvgPrice = CONVERT(MONEY, SUBSTRING(spsx.max_bin_val, 13, 8))
FROM
#SellersTable st --(assuming the id is already unique)
CROSS APPLY (
SELECT
MAX(CONVERT(BINARY(8), sps.date) + CONVERT(BINARY(4), sps.ProductsCount) + CONVERT(BINARY(8), sps.AvgPrice))
FROM
dbo.SellerProductsSnapshots sps
GROUP BY
sps.IdSeller
WHERE
st.id = sps.IdSeller
) spsx (max_bin_val);
September 16, 2018 at 9:40 am
jcelko212 32090 - Tuesday, September 11, 2018 7:45 PMI’m not sure how to get that list of seller_id’s, but I do know the name of the column would not become “id†in one place in your schema and then suddenly become a “seller_id†somewhere else.
Now THAT I most definitely agree with! I'm currently plagued by a 3rd party database where they did that despite my warnings during the initial meeting on development of the database. The have the following column names and they are all for the same thing...
BatchID
Batch_ID
Batch
BatchNum
BatchNo
It's just stupid and certainly shows that the vendor didn't and doesn't actually have their act together. That bit of reflection also explains why their code sucks for performance. No real plan, no real design, and no real concern. At least they didn't try to abbreviate the word "Batch".
--Jeff Moden
Change is inevitable... Change for the better is not.
September 17, 2018 at 8:19 am
Jason A. Long - Friday, September 14, 2018 2:12 PMIn addition to the ROW_NUMBER method offered up by below86, ...
I said to avoid the Row_Number, I avoid it like the plaque. The original requester mentioned using it.
-------------------------------------------------------------
we travel not to escape life but for life not to escape us
Don't fear failure, fear regret.
September 17, 2018 at 8:33 am
below86 - Monday, September 17, 2018 8:19 AMJason A. Long - Friday, September 14, 2018 2:12 PMIn addition to the ROW_NUMBER method offered up by below86, ...I said to avoid the Row_Number, I avoid it like the plaque. The original requester mentioned using it.
You're right... Apparently I read through the existing replies too quickly. Sorry about that.
That said, if the correct "POC" index is in place the ROW_NUMBER() method can be an extremity performant solution... As good or better than any of the other solutions that have been offered up on this thread. Certainly not something to "avoid like the plague".
September 17, 2018 at 9:16 am
PiMané - Tuesday, September 11, 2018 12:20 PMHi,Is there another way of writing this query without first having to get the MAX date for an id and then get the record for that id?
SELECT t0.IdSeller, t0.ProductsCount, t0.AvgPrice FROM SellerProductsSnapshots t0
INNER JOIN (
SELECT IdSeller, MAX(Date) date FROM SellerProductsSnapshots
WHERE IdSeller IN (SELECT Id FROM #SellersTable)
GROUP BY IdSeller
) t1 ON t0.IdSeller = t1.IdSeller AND t1.date = t0.Date
I could do it with ROW_NUMBER and PARTITION BY id ORDER BY date DESC and get the first row but wouldn't that be the same?
Thanks,
Pedro
This will be the fastest if you have more than about 5 rows per IdSeller and you create the indexes below.CREATE INDEX IX_SellerProductsSnapshots_1 ON SellerProductsSnapshots(IdSeller,[Date]) INCLUDE (ProductsCount,AvgPrice);
CREATE INDEX IX_#SellersTable_1 ON #SellersTable(Id);
;WITH CTE AS
(
SELECT DISTINCT
sps.IdSeller
FROM SellerProductsSnapshots sps
WHERE EXISTS(SELECT *
FROM #SellersTable st
WHERE st.Id = sps.IdSeller)
)
SELECT sps.IdSeller,
sps2.ProductsCount,
sps2.AvgPrice
FROM CTE sps
CROSS APPLY(SELECT TOP(1)
sps2.ProductsCount,
sps2.AvgPrice,
sps2.[date]
FROM SellerProductsSnapshots sps2
WHERE sps2.IdSeller = sps.IdSeller
ORDER BY sps2.Date DESC) AS sps2;
September 17, 2018 at 9:34 am
Jonathan AC Roberts - Monday, September 17, 2018 9:16 AMThis will be the fastest if you have more than about 5 rows per IdSeller and you create the indexes below.
Bold claim... Is there, by chance, a test harness to back it up?
September 17, 2018 at 10:33 am
Jason A. Long - Monday, September 17, 2018 8:33 AMbelow86 - Monday, September 17, 2018 8:19 AMJason A. Long - Friday, September 14, 2018 2:12 PMIn addition to the ROW_NUMBER method offered up by below86, ...I said to avoid the Row_Number, I avoid it like the plaque. The original requester mentioned using it.
You're right... Apparently I read through the existing replies too quickly. Sorry about that.
That said, if the correct "POC" index is in place the ROW_NUMBER() method can be an extremity performant solution... As good or better than any of the other solutions that have been offered up on this thread. Certainly not something to "avoid like the plague".
I've seen it used as a crutch to get unique rows when there are better ways. I'll avoid it like the plague and plaque, I brush often. 😀
-------------------------------------------------------------
we travel not to escape life but for life not to escape us
Don't fear failure, fear regret.
September 17, 2018 at 10:41 am
Jason A. Long - Monday, September 17, 2018 9:34 AMJonathan AC Roberts - Monday, September 17, 2018 9:16 AMThis will be the fastest if you have more than about 5 rows per IdSeller and you create the indexes below.Bold claim... Is there, by chance, a test harness to back it up?
Ok some test data:
IF OBJECT_ID('tempdb..#SellersTable') IS NOT NULL
DROP TABLE #SellersTable
DECLARE @RowsToInsert as int = 1000000
;WITH A(A) AS
(SELECT 'Anything' FROM (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) A(A)),
B(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1 A))-1 FROM A A, A B, A C, A D, A E, A F, A G, A H)
SELECT TOP(@RowsToInsert) N Id
INTO #SellersTable
FROM B
IF OBJECT_ID('dbo.SellerProductsSnapshots') IS NOT NULL
DROP TABLE SellerProductsSnapshots
CREATE TABLE SellerProductsSnapshots
(
IdSeller int NOT NULL,
ProductsCount int NOT NULL,
AvgPrice money NOT NULL,
Date datetime NOT NULL
)
GO
INSERT INTO SellerProductsSnapshots
(
IdSeller,
ProductsCount,
AvgPrice,
Date
)
SELECT Id,
ABS(CHECKSUM(NewId())) % 50 + 5,
ABS(CHECKSUM(NewId())) % 100,
DATEADD(dd,ABS(CHECKSUM(NewId())) % 100,'20180101')
FROM #SellersTable
GO 6
CREATE INDEX IX_SellerProductsSnapshots_1 ON SellerProductsSnapshots(IdSeller,[Date]) INCLUDE (ProductsCount,AvgPrice);
CREATE INDEX IX_#SellersTable_1 ON #SellersTable(Id);
Select query;WITH CTE AS
(
SELECT DISTINCT
sps.IdSeller
FROM SellerProductsSnapshots sps
WHERE EXISTS(SELECT *
FROM #SellersTable st
WHERE st.Id = sps.IdSeller)
)
SELECT sps.IdSeller,
sps2.ProductsCount,
sps2.AvgPrice
FROM CTE sps
CROSS APPLY(SELECT TOP(1)
sps2.ProductsCount,
sps2.AvgPrice,
sps2.[date]
FROM SellerProductsSnapshots sps2
WHERE sps2.IdSeller = sps.IdSeller
ORDER BY sps2.Date DESC) AS sps2;
September 17, 2018 at 11:54 am
Start by adding a POC index...
--POC index...
CREATE NONCLUSTERED INDEX ix_SellerProductsSnapshots_POC
ON dbo.SellerProductsSnapshots (
IdSeller ASC,
Date DESC
)
INCLUDE (
ProductsCount,
AvgPrice
);
Note: setting SSMS to "Discard results after execution" to prevent display rendering from impacting results..
SET NOCOUNT ON;
GO
GO
-- DBCC FLUSHPROCINDB(16) WITH NO_INFOMSGS;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
--EXEC master.dbo.sp_DBCC_DropCleanBuffers_FreeProcCache;
PRINT(CONCAT(CHAR(13), CHAR(10), N' wait a moment...', CHAR(13), CHAR(10)));
WAITFOR DELAY '00:00:01';
GO
-- SET STATISTICS XML ON;
GO
DECLARE @_clock_start DATETIME2(7) = SYSDATETIME(), @_test_name NVARCHAR(70) = N' ';
PRINT(CONCAT(N'+', REPLICATE(N'-', 148), N'+
¦', LEFT(CONCAT(N' Start Time: ', @_clock_start, N' Test Name: ', ISNULL(NULLIF(@_test_name, N' '), N'Test Query 1'),
REPLICATE(N' ', 100)), 148), N'¦', CHAR(13), CHAR(10), N'+', REPLICATE(N'-', 148), N'+')); DECLARE @_data_dump_int INT; SET STATISTICS IO ON;
-- ____________________________________________________________________________________________________________________________________________
-- ?????? place tsql here ????????????????????????????????????????????????????????????
;WITH CTE AS
(
SELECT DISTINCT
sps.IdSeller
FROM SellerProductsSnapshots sps
WHERE EXISTS(SELECT *
FROM #SellersTable st
WHERE st.Id = sps.IdSeller)
)
SELECT sps.IdSeller,
sps2.ProductsCount,
sps2.AvgPrice
FROM CTE sps
CROSS APPLY(SELECT TOP(1)
sps2.ProductsCount,
sps2.AvgPrice,
sps2.Date
FROM SellerProductsSnapshots sps2
WHERE sps2.IdSeller = sps.IdSeller
ORDER BY sps2.Date DESC) AS sps2;
-- ?????? place tsql here ????????????????????????????????????????????????????????????
-- ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
DECLARE @_clock_stop DATETIME2(7) = SYSDATETIME(); SET STATISTICS IO OFF; PRINT(CONCAT(N'+', REPLICATE(N'-', 148), N'+
¦', LEFT(STUFF(CONCAT(N' Finish Time: ', @_clock_stop, N' Duration: ', DATEDIFF(mcs, @_clock_start, @_clock_stop)
/ 1000000.0, N' secs. ', DATEDIFF(mcs, @_clock_start, @_clock_stop) / 1000.0, N' ms.', REPLICATE(N' ', 100)), 76, 3, N''), 148), N'¦
+', REPLICATE(N'-', 148), N'+'));
GO
-- SET STATISTICS XML OFF;
GO
-- DBCC FLUSHPROCINDB(16) WITH NO_INFOMSGS;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
--EXEC master.dbo.sp_DBCC_DropCleanBuffers_FreeProcCache;
PRINT(CONCAT(CHAR(13), CHAR(10), N' wait a moment...', CHAR(13), CHAR(10)));
WAITFOR DELAY '00:00:01';
GO
-- SET STATISTICS XML ON;
GO
DECLARE @_clock_start DATETIME2(7) = SYSDATETIME(), @_test_name NVARCHAR(70) = N' ';
PRINT(CONCAT(N'+', REPLICATE(N'-', 148), N'+
¦', LEFT(CONCAT(N' Start Time: ', @_clock_start, N' Test Name: ', ISNULL(NULLIF(@_test_name, N' '), N'Test Query 2'),
REPLICATE(N' ', 100)), 148), N'¦', CHAR(13), CHAR(10), N'+', REPLICATE(N'-', 148), N'+')); DECLARE @_data_dump_int INT; SET STATISTICS IO ON;
-- ____________________________________________________________________________________________________________________________________________
-- ?????? place tsql here ????????????????????????????????????????????????????????????
WITH
cte_AddRN AS (
SELECT
sps.IdSeller,
sps.ProductsCount,
sps.AvgPrice,
sps.Date,
rn= ROW_NUMBER() OVER (PARTITION BY sps.IdSeller ORDER BY sps.Date DESC)
FROM
dbo.SellerProductsSnapshots sps
)
SELECT
IdSeller = st.Id, arn.ProductsCount, arn.AvgPrice
FROM
#SellersTable st
INNER MERGE JOIN cte_AddRN arn -- (since both tables are order on Id (IdSeller a MERGE join is appropriate...
ON st.Id = arn.IdSeller
WHERE
arn.rn = 1;
-- ?????? place tsql here ????????????????????????????????????????????????????????????
-- ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
DECLARE @_clock_stop DATETIME2(7) = SYSDATETIME(); SET STATISTICS IO OFF; PRINT(CONCAT(N'+', REPLICATE(N'-', 148), N'+
¦', LEFT(STUFF(CONCAT(N' Finish Time: ', @_clock_stop, N' Duration: ', DATEDIFF(mcs, @_clock_start, @_clock_stop)
/ 1000000.0, N' secs. ', DATEDIFF(mcs, @_clock_start, @_clock_stop) / 1000.0, N' ms.', REPLICATE(N' ', 100)), 76, 3, N''), 148), N'¦
+', REPLICATE(N'-', 148), N'+'));
GO
-- SET STATISTICS XML OFF;
GO
-- DBCC FLUSHPROCINDB(16) WITH NO_INFOMSGS;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
--EXEC master.dbo.sp_DBCC_DropCleanBuffers_FreeProcCache;
PRINT(CONCAT(CHAR(13), CHAR(10), N' wait a moment...', CHAR(13), CHAR(10)));
WAITFOR DELAY '00:00:01';
GO
-- SET STATISTICS XML ON;
GO
DECLARE @_clock_start DATETIME2(7) = SYSDATETIME(), @_test_name NVARCHAR(70) = N' ';
PRINT(CONCAT(N'+', REPLICATE(N'-', 148), N'+
¦', LEFT(CONCAT(N' Start Time: ', @_clock_start, N' Test Name: ', ISNULL(NULLIF(@_test_name, N' '), N'Test Query 3'),
REPLICATE(N' ', 100)), 148), N'¦', CHAR(13), CHAR(10), N'+', REPLICATE(N'-', 148), N'+')); DECLARE @_data_dump_int INT; SET STATISTICS IO ON;
-- ____________________________________________________________________________________________________________________________________________
-- ?????? place tsql here ????????????????????????????????????????????????????????????
SELECT
IdSeller = st.Id,
ProductsCount = CONVERT(INT, SUBSTRING(spsx.max_bin_val, 9, 4)),
AvgPrice = CONVERT(MONEY, SUBSTRING(spsx.max_bin_val, 13, 8))
FROM
#SellersTable st
CROSS APPLY (
SELECT
MAX(CONVERT(BINARY(8), sps.Date) + CONVERT(BINARY(4), sps.ProductsCount) + CONVERT(BINARY(8), sps.AvgPrice))
FROM
dbo.SellerProductsSnapshots sps
WHERE
st.Id = sps.IdSeller
) spsx (max_bin_val);
-- ?????? place tsql here ????????????????????????????????????????????????????????????
-- ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
DECLARE @_clock_stop DATETIME2(7) = SYSDATETIME(); SET STATISTICS IO OFF; PRINT(CONCAT(N'+', REPLICATE(N'-', 148), N'+
¦', LEFT(STUFF(CONCAT(N' Finish Time: ', @_clock_stop, N' Duration: ', DATEDIFF(mcs, @_clock_start, @_clock_stop)
/ 1000000.0, N' secs. ', DATEDIFF(mcs, @_clock_start, @_clock_stop) / 1000.0, N' ms.', REPLICATE(N' ', 100)), 76, 3, N''), 148), N'¦
+', REPLICATE(N'-', 148), N'+'));
GO
-- SET STATISTICS XML OFF;
GO
The results
wait a moment...
+----------------------------------------------------------------------------------------------------------------------------------------------------+
¦ Start Time: 2018-09-17 13:40:04.7958330 Test Name: Test Query 1 ¦
+----------------------------------------------------------------------------------------------------------------------------------------------------+
Table 'SellerProductsSnapshots'. Scan count 1000001, logical reads 3196940, physical reads 1, read-ahead reads 4719, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table '#SellersTable_______________________________________________________________________________________________________0000000049C6'. Scan count 1, logical reads 2733, physical reads 0, read-ahead reads 16, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
+----------------------------------------------------------------------------------------------------------------------------------------------------+
¦ Finish Time: 2018-09-17 13:40:08.8240652 Duration: 4.028232 secs. 4028.232000 ms. ¦
+----------------------------------------------------------------------------------------------------------------------------------------------------+
wait a moment...
+----------------------------------------------------------------------------------------------------------------------------------------------------+
¦ Start Time: 2018-09-17 13:40:10.0431337 Test Name: Test Query 2 ¦
+----------------------------------------------------------------------------------------------------------------------------------------------------+
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'SellerProductsSnapshots'. Scan count 1, logical reads 4718, physical reads 1, read-ahead reads 4711, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table '#SellersTable_______________________________________________________________________________________________________0000000049C6'. Scan count 1, logical reads 2733, physical reads 0, read-ahead reads 16, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
+----------------------------------------------------------------------------------------------------------------------------------------------------+
¦ Finish Time: 2018-09-17 13:40:12.2462594 Duration: 2.203126 secs. 2203.126000 ms. ¦
+----------------------------------------------------------------------------------------------------------------------------------------------------+
wait a moment...
+----------------------------------------------------------------------------------------------------------------------------------------------------+
¦ Start Time: 2018-09-17 13:40:13.5003326 Test Name: Test Query 3 ¦
+----------------------------------------------------------------------------------------------------------------------------------------------------+
Table '#SellersTable_______________________________________________________________________________________________________0000000049C6'. Scan count 1, logical reads 2733, physical reads 0, read-ahead reads 16, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'SellerProductsSnapshots'. Scan count 1, logical reads 4718, physical reads 1, read-ahead reads 4711, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
+----------------------------------------------------------------------------------------------------------------------------------------------------+
¦ Finish Time: 2018-09-17 13:40:15.2564298 Duration: 1.756097 secs. 1756.097000 ms. ¦
+----------------------------------------------------------------------------------------------------------------------------------------------------+
Viewing 15 posts - 1 through 15 (of 22 total)
You must be logged in to reply to this topic. Login to reply