T-SQL GROUP BY: Eliminate multiple occurrences - sql

How can I prevent multiple identical price rows in the below query due to different date values? I only need to show the first occurrence of a record where price is different than the previous ones but skip until there is another change to the price field.
SELECT STOCKID, PRICE, TRANSTYPENAME, TRANSDATE
FROM VE_STOCKTRANS
WHERE STOCKID = 6000 AND TANSTYPE IN (3500,3553)
GROUP BY PRICE, STOCKID, TRANSTYPENAME, TRANSDATE
i.e. I need to omit the multiple 35.83333333 rows and only take the first occurrence of it (24/4/2018)
Image reply to Gordon's Answer:

You can get first TRANSDATE like that:
SELECT STOCKID, PRICE, TRANSTYPENAME, min(TRANSDATE)
FROM VE_STOCKTRANS
WHERE STOCKID = 6000 AND TRANSTYPE IN (3500,3553)
GROUP BY PRICE, STOCKID, TRANSTYPENAME

SELECT STOCKID, DISTINCT(PRICE), TRANSTYPENAME, TRANSDATE)
FROM VE_STOCKTRANS
WHERE STOCKID = 6000 AND TANSTYPE IN (3500,3553)
GROUP BY PRICE, STOCKID, TRANSTYPENAME, TRANSDATE
This might helps..

Use LAG() to see if the price changed:
SELECT STOCKID, PRICE, TRANSTYPENAME, TRANSDATE
FROM (SELECT st.*,
LAG(st.price) OVER (PARTITION BY stockId ORDER BY transdate) as prev_price
FROM VE_STOCKTRANS st
WHERE STOCKID = 6000 AND TANSTYPE IN (3500, 3553)
) st
WHERE prev_price IS NULL OR prev_price <> price;
EDIT:
If you just want a list of the first time that each price is seen (which is quite different from changes in prices, as you have noted and described in the question), use row_number():
SELECT STOCKID, PRICE, TRANSTYPENAME, TRANSDATE
FROM (SELECT st.*,
ROW_NUMBER() OVER (PARTITION BY st.stockId, st.price ORDER BY st.transdate) as seqnum
FROM VE_STOCKTRANS st
WHERE STOCKID = 6000 AND TANSTYPE IN (3500, 3553)
) st
WHERE seqnum = 1;

Related

Conditioned based Running total

I am rewriting this Post!
This is my Query
SELECT ITEMNUM,
STORELOC,
TRANSDATE,
ACTUALDATE,
CONDITIONCODE,
ISSUETYPE,
QUANTITY,
CURBAL,
'' AS Balnce_In_Condition_02,
ENTERBY,
MEMO,
DOCNUM
FROM MATUSETRANS
WHERE ITEMNUM = 'Z95-04-BHH811550'
AND STORELOC ='04'
AND TRANSDATE >'01-JAN-19'
ORDER BY TRANSDATE DESC;
and it is my result
I need running Balance_In_Condition_02 which I write separately in image. Basically my need is to create a Inventory report of Items which is issue from CONDITIONCODE 01 while the same item was present in CONDITIONCODE 02. Items are frequently ISSUE and RETURN. Hope you can understand my question
At first sum quantity conditionally, only for rows with conditioncode = '02', like in my inner query. Then use analytic lead() to fill values for the rest of rows.
select itemnum, storeloc, transdate, quantity, conditioncode,
nvl(bal02, lead(bal02) ignore nulls
over (partition by itemnum order by transdate desc)) balance02
from (
select itemnum, storeloc, transdate, quantity, conditioncode,
case conditioncode when '02' then sum(quantity)
over (partition by itemnum order by transdate desc) end bal02
from matusetrans mtu
where itemnum = 'Z95-04-B806073A' and storeloc = '04')
order by transdate desc;
dbfiddle
I am not sure if you are summing all quantities or only 02 rows, because in your example these sums are equal. In this second case instead of sum(quantity) use:
sum(case conditioncode when '02' then quantity end) over (...

Tweaking a Query - looking for duplicates within a certain day range

I posted a question similar to this, and got an answer, but the answer isn't configurable - my fault I should have been more clear, so I'll try again.
I have a table where TABLENAME has the following information - OrderDate, OrderNumber, CustomerID, ProductSKU, ProductName exist. This table has lines for invoices. So an order will have a data line for every item in the order.
I want to know, which customers have ordered the same item, more than once, where the order is within 90 of any other order of that same product by that customer, after a specific date. Same product in the same order number do not count. The catch is that I want "more than once" to be configurable, so if I need to see 3 or more, or 4 or more I can adjust AND I want to see the counts. Here's the query I have so far, which I think gives me the items and the counts - but not the 90 day thing:
EDITED: I don't think the former version gave me the right counts
SELECT customerid, productsku, productname, count(distinct ordernumber) FROM tablename
WHERE orderdate >'2017-11-01'
GROUP BY customerid, productsku, productname
HAVING COUNT(distinct ordernumber) > 2
Try doing this. it'll go back 90 days
declare #date date = '2017-11-01'
SELECT customerid, productsku, productname, count(distinct ordernumber) FROM tablename
WHERE orderdate >= dateadd(DD,-90,#date) and orderdate <= #date
GROUP BY customerid, productsku, productname
HAVING COUNT(distinct ordernumber) > 1
yes that is what I was doing in the first query. so this might be a really crappy way of doing it but without seeing any data it was kind of tough. this query shows gives you the order dates as well. hope it helps
WITH DupsWithin90Days (customerid,productsku,productname,orderdate,num)
as
(
select customerid,productsku,productname,orderdate ,count(*) num from (
SELECT X.customerid, X.productsku, X.productname,X.ORDERDATE,ROW_NUMBER() OVER (partition by x.customerid,x.orderdate order by x.orderdate) rownum
FROM
(
SELECT T1.customerid, T1.productsku, T1.productname,T1.ORDERDATE
FROM TABLENAME1 T1
) X
JOIN
(
SELECT T2.customerid, T2.productsku, T2.productname,T2.ORDERDATE
FROM
TABLENAME1 T2
) Y
ON X.customerid = Y.customerid AND X.orderdate >= dateadd(DD,-90,Y.orderdate)
) dup
where rownum > 1
group by customerid,productsku,productname,orderdate
)
select customerid,productsku,productname,orderdate
from DupsWithin90Days
order by customerid ,orderdate desc

Build SQL query with JOIN and limits

Help me please build PostgreSQL query.
There are 2 tables: products(id, title) and prices(id, product_id, price_type, moment, value)
moment - timestamp, can be in past or future
Assume that price_type has only two option: retail or purchase
But one product may has many retail prices with different moments.
I need select all products with actual retail and purchase prices, where moment less than now.
It's I can done
SELECT
products.id,
products.title_translations AS title,
retail_prices.moment AS ret_moment,
pur_prices.value AS purchase,
retail_prices.value AS retail
FROM products
LEFT OUTER JOIN prices AS pur_prices ON products.id=pur_prices.product_id AND pur_prices.price_type='purchase' AND pur_prices.moment<current_timestamp
LEFT OUTER JOIN prices AS retail_prices ON products.id=retail_prices.product_id AND retail_prices.price_type='retail' AND retail_prices.moment<current_timestamp
ORDER BY products.id;
It works, but returns
product with all prices, but I need only last prices(by moment).
Just use ROW_NUMBER to find what is the last price before current time
with last_prices as (
SELECT
products.id,
products.title_translations AS title,
prices.moment,
prices.value,
prices.price_type,
ROW_NUMBER() OVER (PARTITION BY product_id, price_type
ORDER BY moment DESC) as rn
FROM products
LEFT JOIN prices
ON products.id = prices.product_id
WHERE moment < now()
)
SELECT id, title,
MAX(CASE WHEN price_type = 'retail'
THEN moment
END) as retail_moment,
MAX(CASE WHEN price_type = 'retail'
THEN value
END) as retail_price,
MAX(CASE WHEN price_type = 'purchase'
THEN moment
END) as purchase_moment,
MAX(CASE WHEN price_type = 'purchase'
THEN value
END) as purchase_price
FROM last_prices
WHERE rn = 1
GROUP BY id, title
ORDER BY id
To keep things organized, and straight in my mind, I'd use CTEs to generate two subsets of price data, one for purchase one for retail and assign a row number in ascending sequence with the lowest number having the most recent moment less than the currenttimestmap. And then when we join to these ctes, we only return the lowest number assigned.
With Pur_prices as (SELECT P.*, row_Number() over (partition by product_ID order by moment desc) RN
FROM prices P
WHERE price_Type = 'purchase'
and p.moment < current_timestamp)
, Retail_prices as (SELECT P.*, row_Number() over (partition by product_ID order by moment desc) RN
FROM prices P
WHERE price_Type = 'retail'
and p.moment < current_timestamp)
SELECT
p.id,
p.title_translations AS title,
rp.moment AS ret_moment,
rp.value AS retail,
pp.moment AS Pur_moment,
pp.value AS purchase
FROM products p
LEFT JOIN pur_prices pp
ON p.id=pp.product_id
AND pp.RN = 1 --Only show the most recent price less than current time
LEFT JOIN retail_prices rp
ON p.id=rp.product_id
AND RP.RN = 1 --Only show the most recent price less than current time
ORDER BY p.id;
The end result should be all products regardless if they have a retail or purchase price; but if they do show the retail/purchase pricing for the most recent moment before now. My only concern is this implies all pricing has a moment they start (no null values allowed!)
You may be wanting it be ordered with respect to moment in descending order.
Change
ORDER BY products.id;
to
ORDER BY product.id ASC, moment DESC;

Incremental count of duplicates

The following query displays duplicates in a table with the qty alias showing the total count, eg if there are five duplicates then all five will have the same qty = 5.
select s.*, t.*
from [Migrate].[dbo].[Table1] s
join (
select [date] as d1, [product] as h1, count(*) as qty
from [Migrate].[dbo].[Table1]
group by [date], [product]
having count(*) > 1
) t on s.[date] = t.[d1] and s.[product] = t.[h1]
ORDER BY s.[product], s.[date], s.[id]
Is it possible to amend the count(*) as qty to show an incremental count so that five duplicates would display 1,2,3,4,5?
The answer to your question is row_number(). How you use it is rather unclear, because you provide no guidance, such as sample data or desired results. Hence this answer is rather general:
select s.*, t.*,
row_number() over (partition by s.product order by s.date) as seqnum
from [Migrate].[dbo].[Table1] s join
(select [date] as d1, [product] as h1, count(*) as qty
from [Migrate].[dbo].[Table1]
group by [date], [product]
having count(*) > 1
) t
on s.[date] = t.[d1] and s.[product] = t.[h1]
order by s.[product], s.[date], s.[id];
The speculation is that the duplicates are by product. This enumerates them by date. Some combination of the partition by and group by is almost certainly what you need.

SQL query for Pricing analysis

Facing issue to find the Min and Max pricing status on the column YearMonth,
Below is my table data
YearMonth STATE ProductGroup LocaProdname Price
201407 MH AIRTEL AIRTEL-3G 10,000
201208 GJ IDEA IDEA-3G 1,200
201406 WB AIRCEL AIRCEL PERPAID 5,866
201407 DL TATA DOCOMA TATA LANDLINE 8,955
201207 KAR VODAFONE VODAFONE-3G 7,899
201312 MH AIRTEL AIRTEL-3G 15,000
201408 GJ IDEA IDEA-3G 25,000
I require below output:
YearMonth STATE ProductGroup LocaProdname Price Indictor-YEAR
201407 MH AIRTEL AIRTEL-3G 10,000 MAX
201312 MH AIRTEL AIRTEL-3G 15,000 MIN
201408 GJ IDEA IDEA-3G 25,000 MAX
201208 GJ IDEA IDEA-3G 1,200 MIN
I need the Max yearmonth and min Year values values.
If I understand correctly, you can do this with row_number():
select YearMonth, STATE, ProductGroup, LocaProdname, Price,
(case when seqnum_asc = 1 then 'MIN' else 'MAX' end) as Indicator
from (select d.*,
row_number() over (partition by state, productgroup, localprodname
order by price asc) as seqnum_asc,
row_number() over (partition by state, productgroup, localprodname
order by pricedesc) as seqnum_desc
from data
) d
where seqnum_asc = 1 or seqnum_desc = 1;
EDIT:
Does this do what you want?
select YearMonth, STATE, ProductGroup, LocaProdname, Price,
(case when seqnum_asc = 1 then 'MIN' else 'MAX' end) as Indicator
from (select d.*,
row_number() over (partition by YearMonth
order by price asc) as seqnum_asc,
row_number() over (partition by YearMOnth
order by pricedesc) as seqnum_desc
from data
) d
where seqnum_asc = 1 or seqnum_desc = 1;
Please use Row_number with partition BY and remove unwanted code as per your need,
SELECT yearmonth,state,productgroup,locaprodname,price,operation
FROM (
SELECT * FROM (SELECT p.yearmonth,p.state,p.productgroup,p.locaprodname,p.price,'MAX' AS Operation,
Row_number() OVER( partition BY p.productgroup, p.locaprodname
ORDER BY p.price DESC) AS Row
FROM pricingtest p) AS Maxx
WHERE Maxx.row = 1
UNION ALL
SELECT * FROM (SELECT p.yearmonth,p.state,p.productgroup,p.locaprodname,p.price,'MIN' AS Operation,
Row_number() OVER( partition BY p.productgroup, p.locaprodname
ORDER BY p.price ASC) AS Row
FROM pricingtest p) AS Minn
WHERE Minn.row = 1
) AS whole
ORDER BY yearmonth,productgroup
This can be done by finding the MAX/MIN values associated with the LocaProdname,ProductGroup and State then joining in on the table where everything matches. See below, or view the fiddle at http://sqlfiddle.com/#!3/4d6bd/2
NOTE: I've added in HAVING COUNT(*) > 1 as you seem to only want ones which have changed price. (Ie. Have more than 1 entry)
SELECT p.YearMonth
,p.State
,p.ProductGroup
,p.LocaProdname
,p.Price
,CASE
WHEN p.Price = a.MaxPrice
THEN 'MAX'
WHEN p.Price = a.MinPrice
THEN 'MIN'
END AS [Indicator-YEAR]
FROM PricingTest p
INNER JOIN (
SELECT LocaProdname
,ProductGroup
,State
,MAX(Price) AS MaxPrice
,MIN(Price) AS MinPrice
FROM pricingTest
GROUP BY LocaProdname
,ProductGroup
,State
HAVING COUNT(*) > 1
) a ON a.LocaProdname = p.LocaProdname
AND a.ProductGroup = p.ProductGroup
AND a.State= p.State
AND (
a.MaxPrice = p.Price
OR a.MinPrice = p.Price
)
ORDER BY LocaProdname
EDIT: Or I just noticed it's the max/min YearMonth the user might be looking, if this is the case check out http://sqlfiddle.com/#!3/4d6bd/4 It is basically just replacing all references to Price to YearMonth.
Once you get the last and first record you can UNION results:
SELECT t.*, 'MIN' AS Indicator
FROM
myTable t LEFT JOIN
myTable t2 ON t.YearMonth = t2.YearMonth AND t2.price < t.price
WHERE t2.YearMonth IS NULL
UNION
SELECT t.*, 'MAX' AS Indicator
FROM
myTable t LEFT JOIN
myTable t2 ON t.YearMonth = t2.YearMonth AND t2.price > t.price
WHERE t2.YearMonth IS NULL
If you have several records with same highest price, above query will return all of them. Also if you only have one record in a month, it will be returned twice as both MIN and MAX.