Get Last Change Date in SQL - sql

StockCode TimeStamp UnitPrice TrnDate
---------------------------------------------------------------
360120 0x000000000DBE9EED 16.8000 2015-02-13 0:00:00.000
360120 0x000000000DBEE175 16.8000 2015-02-17 00:00:00.000
360120 0x000000000DC177AC 16.8000 2015-02-24 00:00:00.000
360120 0x000000000DC1EEFB 16.0000 2015-02-25 00:00:00.000
360120 0x000000000DC1E9BE 16.8000 2015-02-25 00:00:00.000
360120 0x000000000DC261CE 16.0000 2015-02-27 00:00:00.000
360120 0x000000000DC2628D 16.8000 2015-02-27 00:00:00.000 **
360120 0x000000000DFC46EA 16.8000 2015-03-02 00:00:00.000
The query needs to return 2015-02-27 where price went form 16.000 to 16.8000,
which is the last price change date for this item in the price history table.
The way I wrote it, the query it only works if the price didn't change back to a price that exists in the table for that stock code.
SELECT
MAX(MinTrnDate) as LastTrnDate
FROM
(SELECT
a.UnitPrice, MIN(TrnDate) as MinTrnDate
FROM
(SELECT
ph.StockCode,
CONVERT(DECIMAL(18,4), (ph.InvoiceValue/ ph.InvoiceQty)) as UnitPrice,
ph.TrnDate
FROM
ArSalesMove ph
JOIN
ArCustomer c WITH(NOLOCK) on ph.Customer = c.Customer
WHERE
LTRIM(RTRIM(ph.StockCode)) = '360120'
AND (ph.InvoiceValue/ ph.InvoiceQty) > 0
AND c.PriceCode = 'A') AS a
WHERE
a.UnitPrice > 0
GROUP BY a.UnitPrice) AS b
HAVING
MIN(CONVERT(DECIMAL(18, 2), UnitPrice)) <> MAX(CONVERT(DECIMAL(18, 2), UnitPrice)) --Used to exclude stockcodes with no change...

For SQL Server 2012 or newer:
;WITH
cte AS
(
SELECT *,
LAG(UnitPrice, 1) OVER (PARTITION BY StockCode ORDER BY TrnDate) AS LastPrice
FROM PriceHistory
)
SELECT StockCode, MAX(TrnDate)
FROM cte
WHERE UnitPrice != LastPrice
GROUP BY StockCode
For SQL Server 2005 and up:
;WITH
cte AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY StockCode ORDER BY TrnDate) AS RowNumber
FROM PriceHistory
)
SELECT c2.StockCode, MAX(c2.TrnDate)
FROM cte c1
INNER JOIN cte c2 ON c1.StockCode = c2.StockCode AND c1.RowNumber + 1 = c2.RowNumber
WHERE c1.UnitPrice != c2.UnitPrice
GROUP BY c2.StockCode

Related

SQL: How to get Max Order number row from multiple rows

How can I Select Max Order_number row from given multiple rows? per example below results 9010305604 is the max number. I need to select 9010305604row
SELECT DISTINCT
CUST.PRIMARY_EMAIL_ADDRESS [EMAIL],
OD.ORDER_NO [ORDER_NUMBER],
CUST.MASTER_CUSTOMER_ID [CUSTOMER ID],
CUST.SUB_CUSTOMER_ID,
CUST.USR_ORIG_JOIN_DATE,
OD.CYCLE_END_DATE
, OD.RATE_CODE
,OD.CYCLE_BEGIN_DATE
--, OD.ORIGINAL_ORDER_NO
FROM CUSTOMER CUST (NOLOCK)
INNER JOIN ORDER_DETAIL OD (NOLOCK)
ON CUST.MASTER_CUSTOMER_ID = OD.SHIP_MASTER_CUSTOMER_ID
AND OD.SHIP_SUB_CUSTOMER_ID = CUST.SUB_CUSTOMER_ID
INNER JOIN ORDER_MASTER OM (NOLOCK)
ON OD.ORDER_NO = OM.ORDER_NO
INNER JOIN PRODUCT PROD
ON [PROD].[PRODUCT_ID] = [OD].[PRODUCT_ID]
WHERE OD.SUBSYSTEM = 'MBR'
AND OD.PRODUCT_CODE IN ('PROFESSIONAL')
AND OD.LINE_STATUS_CODE = 'A'
-- AND OD.CYCLE_BEGIN_DATE <= GETDATE()
AND OD.CYCLE_END_DATE >= GETDATE()
--AND OD.ORIGINAL_ORDER_NO IS NULL
AND CUST.PRIMARY_EMAIL_ADDRESS IS NOT NULL
AND CUST.USR_ORIG_JOIN_DATE >cast(DATEADD(day, -120,GETDATE()) AS DATE)
AND OM.USR_BULK_ORDER_NO IS NULL
AND MASTER_CUSTOMER_ID = '4655302'
EMAIL ORDER_NUMBER CUSTOMER ID SUB_CUSTOMER_ID USR_ORIG_JOIN_DATE CYCLE_END_DATE RATE_CODE CYCLE_BEGIN_DATE
sannyastari#gmail.com 9010305603 4655302 0 2020-11-21 00:00:00.000 2020-12-31 00:00:00.000 1YR 2020-11-21 00:00:00.000
sannyastari#gmail.com 9010305604 4655302 0 2020-11-21 00:00:00.000 2021-12-31 00:00:00.000 1YR 2021-01-01 00:00:00.000
Use a TOP query?
SELECT DISTINCT TOP 1
CUST.PRIMARY_EMAIL_ADDRESS [EMAIL],
OD.ORDER_NO [ORDER_NUMBER],
...
(rest of your query)
ORDER BY
OD.ORDER_NO DESC;

add missing month in sales

I have a sales table with below values.
TransactionDate,CustomerID,Quantity
2020-01-01,1234,5
2020-07-01,1234,9
2020-03-01,3241,8
2020-07-01,3241,4
As you can see first purchase was for CustomerID = 1234 in Jan 2020 and for CustomerID = 3241 in MAR 2020.
I want on output where in all the date should be filled up with 0 purchase value.
means if there is no sale between Jan and July Then output should be as below.
TransactionDate,CustomerID,Quantity
2020-01-01,1234,5
2020-02-01,1234,0
2020-03-01,1234,0
2020-04-01,1234,0
2020-05-01,1234,0
2020-06-01,1234,0
2020-07-01,1234,9
2020-03-01,3241,8
2020-04-01,3241,0
2020-05-01,3241,0
2020-06-01,3241,0
2020-07-01,3241,4
You can use a recursive query to create the missing dates per customer.
with recursive dates (customerid, transactiondate, max_transactiondate) as
(
select customerid, min(transactiondate), max(transactiondate)
from sales
group by customerid
union all
select customerid, dateadd(month, 1, transactiondate), max_transactiondate
from dates
where transactiondate < max_transactiondate
)
select
d.customerid,
d.transactiondate,
coalesce(s.quantity, 0) as quantity
from dates d
left join sales s on s.customerid = d.customerid and s.transactiondate = d.transactiondate
order by d.customerid, d.transactiondate;
This is a convenient place to use a recursive CTE. Assuming all your dates are on the first of the month:
with cr as (
select customerid, min(transactiondate) as mindate, max(transactiondate) as maxdate
from t
group by customerid
union all
select customerid, dateadd(month, 1, mindate), maxdate
from cr
where mindate < maxdate
)
select cr.customerid, cr.mindate as transactiondate, coalesce(t.quantity, 0) as quantity
from cr left join
t
on cr.customerid = t.customerid and
cr.mindate = t.transactiondate;
Here is a db<>fiddle.
Note that if you have more than 100 months to fill in, then you will need option (maxrecursion 0).
Also, this can easily be adapted if the dates are not all on the first of the month. But you would need to explain what the result set should look like in that case.
[EDIT] Based on what other posted I updated the code.
;with
min_date_cte(MinTransactionDate, MaxTransactionDate) as (
select min(TransactionDate), max(TransactionDate) from tsales),
unq_yrs_cte(year_int) as (
select distinct year(TransactionDate) from tsales),
unq_cust_cte(CustomerID) as (
select distinct CustomerID from tsales)
select datefromparts(uyc.year_int, v.month_int, 1) TransactionDate,
ucc.CustomerID,
isnull(t.Quantity, 0) Quantity
from min_date_cte mdc
cross join unq_yrs_cte uyc
cross join unq_cust_cte ucc
cross join (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) v(month_int)
left join tsales t on datefromparts(uyc.year_int, v.month_int, 1)=t.TransactionDate
and ucc.CustomerID=t.CustomerId
where
datefromparts(uyc.year_int, v.month_int, 1)>=mdc.MinTransactionDate
and datefromparts(uyc.year_int, v.month_int, 1)<=mdc.MaxTransactionDate;
Results
TransactionDate CustomerID Quantity
2020-01-01 1234 5
2020-01-01 3241 0
2020-02-01 1234 0
2020-02-01 3241 0
2020-03-01 1234 0
2020-03-01 3241 8
2020-04-01 1234 0
2020-04-01 3241 0
2020-05-01 1234 0
2020-05-01 3241 0
2020-06-01 1234 0
2020-06-01 3241 0
2020-07-01 1234 9
2020-07-01 3241 4
You can make use of recursive query:
WITH cte1 as
(
select customerid, min([TransactionDate]) as Monthly_date, max([TransactionDate]) as end_date from calender_table
group by customerid
union all
select customerid, dateadd(month, 1, Monthly_date), end_date from cte1
where Monthly_date < end_date
)
select a.Monthly_date, a.customerid,coalesce(b.quantity, 0) from cte1 a left outer join calender_table b
on (a.Monthly_date = b.[TransactionDate] and a.customerid = b.customerid)
order by a.customerid, a.Monthly_date;

New column which indicate min date from group of value

I would like to add new column which will be indicating min value of subgroup.
Id ShopId OrderDate
12232018 12229018 2011-01-01 00:00:00.000
12232018 12229018 2012-01-01 00:00:00.000
12232018 12394018 2012-02-02 00:00:00.000
12232018 11386005 2012-03-01 00:00:00.000
12232018 14347023 2012-04-02 00:00:00.000
12232018 14026026 2014-03-16 00:00:00.000
Here is the result I want to get:
NewCol Id ShopId OrderDate
1 12232018 12229018 2011-01-01 00:00:00.000
1 12232018 12229018 2012-01-01 00:00:00.000
0 12232018 12394018 2012-02-02 00:00:00.000
0 12232018 11386005 2012-03-01 00:00:00.000
0 12232018 14347023 2012-04-02 00:00:00.000
0 12232018 14026026 2014-03-16 00:00:00.000
Because ShopId have min OrderDate for Id I would like to assign '1' to this ShopId.
You can use min with windowing function to get this as below:
select NewCol = Case when orderdate = min(orderdate) over() then 1 else 0 end,*
from yourtable
--Probably you might require to add Partition by Id or shopId depends on requirement
Try this:
SELECT Id, ShopId, OrderDate,
CASE
WHEN MIN(OrderDate) OVER (PARTITION BY Id, ShopId) =
MIN(OrderDate) OVER (PARTITION BY Id) THEN 1
ELSE 0
END AS NewCol
FROM mytable
The query uses windowed version of MAX in order to compare the minimum-per-Id OrderDate to the minimum-per- (Id, ShopId) date. If these two values are the same, then we mark the corresponding (Id, ShopId) partition with 1.
Demo here
Less elegant than the others, but is ANSI
select MyTable.*, case when a1.mindate = orderdate then 1 else 0 end as NewCol
from MyTable
inner join
(
select id, min(orderdate) as mindate
from Mytable
group by id
) a1
on a1.id = MyTable.id
Use min with orderdate on ShopId and use that in the Case When statement like this:-
Select case when (a.OrderDate=b.min_order_dt) then 1 else 0 end as NewCol, a.*
from
your_table_name a
inner join
(
SELECT ShopId, min(OrderDate) as min_order_dt
from
your_table_name
group by shop_id
) b
on a.ShopId=b.ShopId;
Try this
select case when t2.ShopId is null then 0 else 1 end as newcol,t1.id,
t1.ShopiId,t1.OrderDate
from table as t1 left join
(
select ShopId,min(OrderDate) as OrderDate from table
group by ShopId
) as t2 on t1.ShopId=t2.ShopId and t1.OrderDate=t2.OrderDate

Calculate discount between weeks

I have a table containing product price data, like that:
ProductId RecordDate Price
46 2015-01-17 14:35:05.533 112.00
47 2015-01-17 14:35:05.533 88.00
45 2015-01-17 14:35:05.533 134.00
I have been able to group data by week and product, with this query:
SET DATEFIRST 1;
SELECT DATEADD(WEEK, DATEDIFF(WEEK, 0, [RecordDate]), 0) AS [Week], ProductId, MIN([Price]) AS [MinimumPrice]
FROM [dbo].[ProductPriceHistory]
GROUP BY DATEADD(WEEK, DATEDIFF(WEEK, 0, [RecordDate]), 0), ProductId
ORDER BY ProductId, [Week]
obtaining this result:
Week Product Price
2015-01-12 00:00:00.000 1 99.00
2015-01-19 00:00:00.000 1 98.00
2015-01-26 00:00:00.000 1 95.00
2015-02-02 00:00:00.000 1 95.00
2015-02-09 00:00:00.000 1 95.00
2015-02-16 00:00:00.000 1 95.00
2015-02-23 00:00:00.000 1 80.00
2015-03-02 00:00:00.000 1 97.00
2015-03-09 00:00:00.000 1 85.00
2015-01-12 00:00:00.000 2 232.00
2015-01-19 00:00:00.000 2 233.00
2015-01-26 00:00:00.000 2 194.00
2015-02-02 00:00:00.000 2 194.00
2015-02-09 00:00:00.000 2 199.00
2015-02-16 00:00:00.000 2 199.00
2015-02-23 00:00:00.000 2 199.00
2015-03-02 00:00:00.000 2 214.00
Now for each product I'd like to get the difference between the last two week values, so that I can calculate the discount. I don't know how to write this as a SQL Query!
EDIT:
Expected output would be something like that:
Product Price
1 -12.00
2 15.00
Thank you!
since you are using Sql Server 2014 you can use LAG or LEAD window function to do this.
Generate Row number to find the last two weeks for each product.
;WITH cte
AS (SELECT *,
Row_number()OVER(partition BY product ORDER BY weeks DESC)rn
FROM Yourtable)
SELECT product,
price
FROM (SELECT product,
Price=price - Lead(price)OVER(partition BY product ORDER BY rn)
FROM cte a
WHERE a.rn <= 2) A
WHERE price IS NOT NULL
SQLFIDDLE DEMO
Traditional solution, can be used before Sql server 2012
;WITH cte
AS (SELECT *,
Row_number()OVER(partition BY product
ORDER BY weeks DESC)rn
FROM Yourtable)
SELECT a.Product,
b.Price - a.Price
FROM cte a
LEFT JOIN cte b
ON a.Product = b.Product
AND a.rn = b.rn + 1
WHERE a.rn <= 2
AND b.Product IS NOT NULL

T-SQL NULL-Friendly Query

I have a table "tblSalesOrder" in Microsoft T-SQL with some sample records:
SalesOrderID OrderDate ItemID Quantity PromotionCode
====================================================================
1 2014-09-01 100 5 NULL
2 2014-09-01 120 10 ABC
3 2014-09-05 150 7 NULL
4 2014-09-08 200 15 NULL
I need to return NULL-friendly resultset for records which do not exist.
As an example, I want a monthly query for September 2014:
SELECT SalesOrderID, OrderDate, ItemID, Quantity, PromotionCode
FROM tblSalesOrder
WHERE OrderDate = BETWEEN '2014-09-01' AND '2014-09-30'
I need it to return at least 1 row for each day (i.e. 0 valued row, if the entry for that day is not available)
SalesOrderID OrderDate ItemID Quantity PromotionCode
====================================================================
1 2014-09-01 100 5 NULL
2 2014-09-01 120 10 ABC
0 2014-09-02 0 0 0
0 2014-09-03 0 0 0
0 2014-09-04 0 0 0
3 2014-09-05 150 7 NULL
0 2014-09-06 0 0 0
0 2014-09-07 0 0 0
4 2014-09-08 200 15 NULL
0 2014-09-09 0 0 0
...
...
...
0 2014-09-30 0 0 0
master..spt_values is a table in all microsoft sql databases containing 2506 rows, by cross joining, it will have 2506*2506 rows to calculate dates between from and to. Other tables can be used as well, this is just a table used to create the dates. A calendar table would be even easier to use.
The EXCEPT will remove all dates already in use. Then by combining the rows from tblSalesOrder and CTE with union all, empty days will be filled with the required hardcoded values:
DECLARE #from date = '2014-09-01'
DECLARE #to date = '2014-09-30'
;WITH CTE as
(
SELECT top (case when #to < #from then 0 else datediff(day, #from, #to) + 1 end)
dateadd(day, row_number() over (order by (select 1)) - 1, #from) OrderDate
FROM
master..spt_values t1
CROSS JOIN
master..spt_values t2
EXCEPT
SELECT
OrderDate
FROM
tblSalesOrder
)
SELECT
0 SalesOrderID, OrderDate, 0 ItemID, 0 Quantity, '0' PromotionCode
FROM
CTE
UNION ALL
SELECT
SalesOrderID, OrderDate, ItemID, Quantity, PromotionCode
FROM
tblSalesOrder
ORDER BY
OrderDate, SalesOrderId
You can join the a date parameter in an empty select and coalesce the values:
select coalesce(t.SalesOrderID, 0) SalesOrderID
, coalesce(t.OrderDate, d.OrderDate) OrderDate
, coalesce(t.ItemID, 0) ItemID
, coalesce(t.Quantity, 0) Quantity
, coalesce(t.PromotionCode, 0) PromotionCode
from (select #dateParameter OrderDate) d
left
outer
join ( SELECT SalesOrderID, OrderDate, ItemID, Quantity, PromotionCode
FROM tblSalesOrder
) t
on t.OrderDate = d.OrderDate
DECLARE #startDate date= '20140901'
,#endDate date = '20140930';
WITH Calendar as (
SELECT #startDate as OrderDate
UNION ALL
SELECT DATEADD(DAY, 1, OrderDate) as OrderDate
FROM Calendar
WHERE OrderDate < #endDate
)
SELECT coalesce(t.SalesOrderID, 0) SalesOrderID
, coalesce(t.OrderDate, Calendar.OrderDate) OrderDate
, coalesce(t.ItemID, 0) ItemID
, coalesce(t.Quantity, 0) Quantity
, CASE WHEN t.OrderDate IS NULL THEN '0' ELSE t.PromotionCode END as PromotionCode FROM Calendar
LEFT JOIN tblSalesOrder t ON Calendar.OrderDate = t.OrderDate
ORDER BY Calendar.OrderDate, t.SalesOrderID
OPTION (MAXRECURSION 0);