SQL Server - Only Select Latest Date - sql

RDBMS = Microsoft SQL Server
I work for a refrigeration company and we want to do a better job of tracking the cost bottles of refrigerant were bought at for each inventory location. I am trying to create a SQL Query that pulls this information but I am running into some issues. For each inventory location I want to display the last cost refrigerant was bought at for that inventory location.I want to see the latest date we have record of for this location purchasing a specific refrigerant. I have tried using the Max function unsuccessfully and the Row_Number function I have not been able to get work. Any help would be much appreciated.
See below the code sample I am trying to only get to display the Latest Date each inventory location purchased R-22 30 pound jug.
select
lctn_id as Location,
invntryitm_id as InventoryItemID,
invntryitm_nme as InventoryItemName,
prchseordrlst_dte_rqstd as DateRequested,
prchseordrlst_unt_cst as UnitCost
from
invntryitm
join
prchseordrlst on prchseordrlst.invntryitm_rn = invntryitm.invntryitm_rn
join
prchseordr on prchseordr.prchseordr_rn = prchseordrlst.prchseordr_rn
join
lctn on lctn.lctn_rn = prchseordr.lctn_rn
where
invntryitm.invntryitm_nme ='REFRIGERANT R-22 30#'
and lctn_obslte = 'N'
group by
lctn.lctn_id, invntryitm.invntryitm_id, invntryitm.invntryitm_nme,
prchseordrlst.prchseordrlst_unt_cst
order by
lctn_id

I think an analytic/windowing function would give you what you need:
with location_data as (
select
lctn_id as Location,
invntryitm_id as InventoryItemID,
invntryitm_nme as InventoryItemName,
prchseordrlst_dte_rqstd as DateRequested,
prchseordrlst_unt_cst as UnitCost,
max (prchseordrlst_dte_rqstd) over (partition by lctn_id) as max_date
from
invntryitm
JOIN prchseordrlst on prchseordrlst.invntryitm_rn = invntryitm.invntryitm_rn
JOIN prchseordr on prchseordr.prchseordr_rn = prchseordrlst.prchseordr_rn
JOIN lctn on lctn.lctn_rn = prchseordr.lctn_rn
where
invntryitm.invntryitm_nme ='REFRIGERANT R-22 30#' and
lctn_obslte = 'N'
)
select *
from location_data
where max_date = DateRequested
order by Location
Bear in mind that if there is a tie, two location_id records with the same date, then you will get both of them back. If this is an issue, then you probably want row_number() instead of max():
row_number() over (partition by lctn_id order by prchseordrlst_dte_rqstd desc) as rn
And then you would
where rn = 1
to get the first row
The reason I didn't list row_number() first is that max is O(n), and if your data has dates and times, it may be sufficient for what you need.

Related

SQL Server: Generate squashed range data from daily dates by an id

Basically to sum up the goal. I want to generate something using the following data. Where it subtotals like excel where "each change in ... ordered however" generate summary records and only generate summary records and squash date ranges.
All I want are the count records would generate rows and would copy the fee data except for the begin/end fields would be the ranged dates. So first record would be fee 9858 from 3/31 - 4/14 for example. Each count row would be a new fee record.
I am not sure which combination of grouping, partition by.... and such to get what I need or if there is something else I can use. I can provide sql if needed but I am mainly looking for finding the right combination of tools(partition by, grouping, rollup....) that would provide this functionality.
WITH [ag]
AS (SELECT *,
LAG([Fee_ID]) OVER (ORDER BY [FeeTypeID], [FeeBeginDate]) [FirstFee],
LAG([Fee_ID]) OVER (ORDER BY [FeeTypeID], [FeeBeginDate] DESC) [LastFee]
FROM [dbo].[HHTFees]
WHERE [Retailer] = 517),
[agf]
AS (SELECT *,
'Beg' [FeeStopType]
FROM [ag]
WHERE [ag].[FirstFee] <> [ag].[Fee_ID]
OR [ag].[FirstFee] IS NULL),
[agl]
AS (SELECT *,
'End' [FeeStopType]
FROM [ag]
WHERE [ag].[LastFee] <> [ag].[Fee_ID]
OR [ag].[LastFee] IS NULL),
[results]
AS (SELECT *
FROM [agf]
UNION
SELECT *
FROM [agl]),
[indexed]
AS (SELECT *,
ROW_NUMBER() OVER (ORDER BY [results].[FeeTypeID], [results].[FeeBeginDate]) [RowNum]
FROM [results])
SELECT [Starts].[Retailer],
[Starts].[Chain_key],
[Starts].[Fee_ID],
[Starts].[FeeTypeID],
[Starts].[FeeChgTypeID],
[Starts].[Fee],
[Starts].[FeeDescription],
[Starts].[FeeTypeDescription],
[Starts].[FeeBeginDate],
[Ends].[FeeEndDate],
[Starts].[FeeAmt],
[Starts].[HHT_ID],
[Starts].[CreatedBy],
[Starts].[CreatedDate],
[Starts].[ModifiedBy],
[Starts].[ModifiedDate],
[Starts].[MsgID],
[Starts].[ScopeOrder],
[Starts].[Scope],
[Starts].[FirstFee],
[Starts].[LastFee],
[Starts].[FeeStopType],
[Starts].[RowNum]
FROM [indexed] [Starts]
INNER JOIN [indexed] [Ends]
ON [Starts].[Fee_ID] = [Ends].[Fee_ID]
AND [Ends].[RowNum] = [Starts].[RowNum] + 1;
I used lag to find where the fee id changed sorted by feetype / start date. This allowed me to mimic when fee id changed sorted by begin date like excel subtotal. Now to optimize and tweak to fit the set of data.

OVER and partition with multiple columns without resetting the total

I would like to not reset a running total using the OVER clause in SQL server. I have tried several things and searched for an answer very hard, but could not find anything. My query is below, as well as the results.
SELECT
coaID,
fiscalID,
gl.amount,
SUM(gl.amount) OVER (PARTITION BY coaID, fiscalID ORDER BY gl.id) runningTotal
FROM
gl
INNER JOIN dbo.coa ON dbo.gl.coaId = coa.ID
WHERE
gl.coaID IN (
SELECT DISTINCT coa
FROM glCategoryGLAccountLink
WHERE glCategoryGLAccountLink.categoryId = 10001
)
AND gl.companyID=1
ORDER BY coaID, fiscalID
It mostly works, but resets the running total when fiscalID changes. My objective is to allow the running total to continue without being reset for each coaID.
My objective is to allow the running total to continue without being reset for each coaID.
Remove the fiscalID from the partition, ie change this:
SUM(gl.amount) OVER (PARTITION BY coaID, fiscalID ORDER BY gl.id) runningTotal
To:
SUM(gl.amount) OVER (PARTITION BY coaID ORDER BY gl.id) runningTotal
I think you still need to take fiscalid into account for the ordering. So:
SELECT coaID, fiscalID, gl.amount,
SUM(gl.amount) OVER (PARTITION BY coaID ORDER BY fiscalID, gl.id) runningTotal
FROM gl JOIN
dbo.coa c
ON gl.coaId = c.ID
WHERE gl.coaID IN (SELECT gal.coa
FROM glCategoryGLAccountLink gal
WHERE gal.categoryId = 10001
) AND
gl.companyID = 1
ORDER BY coaID, fiscalID;
It is possible that gl.id takes this into account. Your question is not clear on that.
Notes:
Qualify all column references with the table they come from. I would have added the qualifiers, but it is not clear which table they come from.
Table aliases help.
SELECT DISTINCT is not needed with IN. Most databases will ignore it, but you run the risk of the DISTINCT affecting the optimizer.

SQL Server 2008 R2 - How to filter a group of records with conditional logic?

I have the following dataset:
Each sales order line has an item which can be found in various location areas in our warehouse (UPPER, GROUND, FLOOR). What I want is a way to evaluate each sales order line and then pick one location, based on a condition.
The condition would say, if SO line contains a location with FLOOR, pick only that location, else check if it contains GROUND, then pick that, or if it contains neither ground or floor then return UPPER.
I don't want to see multiple location areas for each SO line. What's all the ways this can be done? I'd imagine some form of using a case statement with a HAVING clause?
This can be done using the row_number function by ordering the location areas based on the conditions.
select *
from (select t.*
,row_number() over(partition by so#
order by case when location_area='Floor' then 1
when location_area='GROUND' then 2
else 3 end) rn
from tablename t
) x
where rn = 1
Select coalesce(f.SO, g.SO, u.SO) SO,
coalesce(f.Line, g.Line, u.Line) Line,
coalesce(f.item_code, g.item_code, u.item_code) item_code,
coalesce(f.item_description, g.item_description, u.item_description) item_description,
coalesce(f.SO_Qty, g.SO_Qty, u.SO_Qty) SO_Qty,
coalesce(f.branch_Number, g.branch_Number, u.branch_Number) branch_Number,
coalesce(f.location_area, g.location_area, u.location_area) location_area
from myTable f
full join myTable g
on f.location_area='floor'
and g.SO = f.So
and g.location_area='ground'
full join myTable u
on u.SO = f.So
and u.location_area='upper'

Datediff and aggregate

I am new to SQL so please excuse my lack of knowledge. This is the table i have based on the following statement:
'select S_OPERATION.OPERATIONID, CHANGE_H.SERVICEREQNO, CHANGE_H.UPDATEDDATE
from sunrise.S_OPERATION inner join
CHANGE_H on S_OPERATION.OPERATIONID = CHANGE_H.OPERATIONID
where (S_OPERATION.OPERATIONID = 102005212) OR
(S_OPERATION.OPERATIONID = 102005218) or
(s_operation.operationid = 102005406) or
(s_operation.operationid = 102005401) or
(s_operation.operationid = 102005215)'
enter image description here
I would like to be able to calculate the time difference between events within the same job.
Please note: OperationID=event, Servicereqno=job
My end goal is to calculate the average time taken between each event and export this into a report, but i am having problems getting past the first hurdle.
I have tried the following statement however it does not work:
WITH cteOps AS
(
SELECT
row_number() OVER (PARTITION BY change.servicereqid ORDER BY change.updateddate) seqid,
updateddate,
servicereqid
FROM CHANGE.updateddate, CHANGE.addedby, S_OPERATION.operationid, CHANGE.servicereqid
)
SELECT
DATEDIFF(millisecond, o1.updateddate, o2.updateddate) updateddatediff,
servicereqid
FROM cteOps o1
JOIN cteOps o2 ON o1.seqid=o2.seqid+1 AND o1.servicereqid=o2.servicereqid;
Many thanks in advance.
Your two queries look quite different having different table names, etc. So you'd probably have to adjust my query below to match what you actually have.
You can look into the previous record with LAG. So a query showing all those events with a time difference to the previous one could be:
select
c.updateddate
, c.addedby
, so.operationid
, c.servicereqid
, so.updateddate
, datediff
( millisecond
, lag(so.updateddate) over (partition by c.servicereqid order by so.updateddate)
, so.updateddate
) as updateddatediff
from change c
inner join change_h ch
on c.servicereqid = ch.servicereqno
and ch.operationid in (102005212, 102005218, 102005406, 102005401, 102005215)
inner join s_operation so
on ch.operationid = so.operationid
order by
c.servicereqid,
so.updateddate;
You can build up on this by using it as a derived table (a subquery in a FROM clause).

How to combine this query

In the query
cr is customers,
chh? ise customer_pays,
cari_kod is customer code,
cari_unvan1 is customer name
cha_tarihi is date of pay,
cha_meblag is pay amount
The purpose of query, the get the specisified list of customers and their last date for pay and amount of money...
Actually my manager needs more details but the query is very slow and that is why im using only 3 subquery.
The question is how to combine them ?
I have researched about Cte and "with clause" and "subquery in "where " but without luck.
Can anybody have a proposal.
Operating system is win2003 and sql server version is mssql 2005.
Regards
select cr.cari_kod,cr.cari_unvan1, cr.cari_temsilci_kodu,
(select top 1
chh1.cha_tarihi
from dbo.CARI_HESAP_HAREKETLERI chh1 where chh1.cha_kod=cr.cari_kod order by chh1.cha_RECno) as sontar,
(select top 1
chh2.cha_meblag
from dbo.CARI_HESAP_HAREKETLERI chh2 where chh2.cha_kod=cr.cari_kod order by chh2.cha_RECno) as sontutar
from dbo.CARI_HESAPLAR cr
where (select top 1
chh3.cha_tarihi
from dbo.CARI_HESAP_HAREKETLERI chh3 where chh3.cha_kod=cr.cari_kod order by chh3.cha_RECno) >'20130314'
and
cr.cari_bolge_kodu='322'
or
cr.cari_bolge_kodu='324'
order by cr.cari_kod
You will probably speed up the query by changing your last where clause to:
where (select top 1 chh3.cha_tarihi
from dbo.CARI_HESAP_HAREKETLERI chh3 where chh3.cha_kod=cr.cari_kod
order by chh3.cha_RECno
) >'20130314' and
cr.cari_bolge_kodu in ('322', '324')
order by cr.cari_kod
Assuming that you want both the date condition met and one of the two codes. Your original logic is the (date and code = 322) OR (code = 324).
The overall query can be improved by finding the record in the chh table and then just using that. For this, you want to use the window function row_number(). I think this is the query that you want:
select cari_kod, cari_unvan1, cari_temsilci_kodu,
cha_tarihi, cha_meblag
from (select cr.*, chh.*,
ROW_NUMBER() over (partition by chh.cha_kod order by chh.cha_recno) as seqnum
from dbo.CARI_HESAPLAR cr join
dbo.CARI_HESAP_HAREKETLERI chh
on chh.cha_kod=cr.cari_kod
where cr.cari_bolge_kodu in ('322', '324')
) t
where chh3.cha_tarihi > '20130314' and seqnum = 1
order by cr.cari_kod;
This version assumes the revised logic date/code logic.
The inner subquery select might generate an error if there are two columns with the same name in both tables. If so, then just list the columns instead of using *.