I had received some really good help earlier, and I appreciate it.
I have another record selection snafu.
I have a parameter that I need to set as the end date.
I need to pull the most recent state before the end date from a table titled state_change.
I need to exclude any records from the report who are not in the required states at that period in time.
state is set currently to be state_change.new_state
( {#grouping} = "Orders" and rec_date < {?endDate} and {#state} in [0,2,5] )
OR
( {#grouping} = "Stock" and rec_date < {?endDate} and {#state} in [1,2,3,5,7] )
If I could run a SQL query to pull this information, it would probably work, but I cannot figure out how to do it.
Essentially, I need #state to be:
Select max(new_state)
From state_change
where change_time < {?endDate}
but on a per item level.
Any help would be appreciated.
You'll probably need to use a command object with a parameter for your end date, or create a parameterized stored procedure. The command object will allow you to enter all the sql you need, like joining your results with the max newState value before the end date:
select itemID, new_state, rec_date, max_newState from
(select itemID, new_state, rec_date from table) t inner join
(Select itemID, max(new_state) as max_newState
From state_change
where change_time < {?endDate}
group by itemID) mx on t.itemid = mx.itemID and t.new_state = mx.max_newState
I can't tell if your orders and stock groupings are in the same table, so I'm not sure how you need to limit your sets by the correct state values.
Related
I have been working on this SQL code for a bit and I cannot get it to display like I want. I have an operation that we send parts outside of our business but there is no time stamp on when that operation sent out.
I am taking the previous operation's last labor date and the purchase order creation date to try and find out how long it takes that department to issued a purchase order.
I have tried LAST_Value to add to my query. I have even played with LAG and couldn't get a anything but errors.
SELECT
JobOpDtl.JobNum,
JobOpDtl.OprSeq,
JobOpDtl.OpDtlDesc,
LastValue.ClockInDate,
LastValue.LastValue
FROM Erp.JobOpDtl
LEFT OUTER JOIN Erp.LaborDtl ON
LaborDtl.JobNum = JobOpDtl.JobNum
and LaborDtl.OprSeq = JobOpDtl.OprSeq
LEFT OUTER JOIN (
Select
LaborDtl.JobNum,
LaborDtl.OprSeq,
MAX(LaborDtl.ClockInDate) as ClockInDate,
LAST_VALUE (LaborDtl.ClockInDate) OVER (PARTITION BY OprSeq ORDER BY JobNum) as LastValue
FROM Erp.LaborDtl
GROUP BY
LaborDtl.JobNum,
LaborDtl.OprSeq,
LaborDtl.ClockInDate
) as LastValue ON
JobOpDtl.JobNum = LastValue.JobNum
and JobOpDtl.OprSeq = LastValue.OprSeq
WHERE JobOpDtl.JobNum = 'PA8906'
GROUP BY
JobOpDtl.JobNum,
LastValue.OprSeq,
JobOpDtl.OpDtlDesc,
JobOpDtl.OprSeq,
LastValue.ClockInDate,
LastValue.LastValue
No errors, just not displaying how I am wanting it.
I would like it to display the OperSeq with the previous OperSeq last transaction date.
The basic function you want is LAG (as you suggested) but you need to wrap it in a COALESCE. Here is a sample code that illustrates the concept
SELECT * INTO #Jobs
FROM (VALUES ('P1','Step1', '2019-04-01'), ('P1','Step2', '2019-04-02')
, ('P1','Step3', '2019-04-03'), ('P1','Step4', NULL),
('P2','Step1', '2019-04-01'), ('P2','Step2', '2019-04-03')
, ('P2','Step3', '2019-04-06'), ('P2','Step4', NULL)
) as JobDet(JobNum, Descript, LastDate)
SELECT *
, COALESCE( LastDate, LAG(LastDate,1)
OVER(PARTITION BY JobNum
ORDER BY COALESCE(LastDate,GETDATE()))) as LastValue
FROM #Jobs
ORDER BY JobNum, Descript
DROP TABLE #Jobs
To apply it to your specific problem, I'd suggest using a COMMON TABLE EXPRESSION that replaces LastValue and using that instead of the raw table for your queries.
Your example picture doesn't match any tables you reference in your code (it would help us significantly if you included code that created temp tables matching those referenced in your code) so this is a guess, but it will be something like this:
;WITH cteJob as (
SELECT JobNum, OprSeq, OpDtlDesc, ClockInDate
, COALESCE( LastValue, LAG(LastValue,1)
OVER(PARTITION BY JobNum
ORDER BY COALESCE(LastValue,GETDATE()))) as LastValue
FROM Erp.JobOptDtl
) SELECT *
FROM cteJob as J
LEFT OUTER JOIN LaborDtl as L
on J.JobNum = JobNum
AND J.OprSeq = L.OprSeq
BTW, if you clean up your question to provide a better example of your data (i.e. SELECT INTO sttements like in the start of my answer that produce tables that correspond to the tables in your code instead of an image of an excel file) I might be able to get you closer to what you need, but hopefully this is enough to get you on the right track and it's the best I can do with what you've provided so far.
I got a simple thing to do.
Well, maybe not, but someone somewhere surely can help me out : P
I got a simple data structure that contains
expedition date
delivery date
transaction type
I would need to create a query which could
order the rows by a date specific to the transaction type.
(ie : using the expedition date for transaction of type "selling", and delivery date for transaction of type "purchasing")
I was wondering if there was a more efficient way to do this than
by fetching 2 times the same data with different clause where(while adding a column used to order them(tempDate)) and then using another select to encompass these 2 queries to which I would add the order clause on the tempDate.
--> the initial fetching I would do 2 times works on many tables(many, many, many joins)
Basically my current solution is :
Select * from
(
Select ...
date_exp as dateTemp;
from ...
where conditions* And dateRelatedCondition
UNION
Select ...
date_livraison as dateTemp;
from ...
Where conditions* And NOT(dateRelatedCondition)
) as comboSelect
Order By MIN(comboSelect.dateTemp)
OVER(PARTITION BY(REF_product)),
(REF_product),
comboSelect.dateTemp asc;
*
->Those conditions are the same in both inner Select query
Thank you for your time.
Without the UNION:
dateRelatedCondition should be removed from WHERE and put to the SELECT like:
CASE WHEN dateRelatedCondition THEN date_exp ELSE date_livraison END as dateTemp
Without the subquery:
in ORDER BY you need the same expression in the window function:
Order By MIN(CASE WHEN dateRelatedCondition THEN date_exp ELSE date_livraison END)
OVER(PARTITION BY(REF_product)),
(REF_product),
dateTemp asc
You mean like this?:
ORDER BY CASE
WHEN TransactionType = 'Selling' THEN ExpeditionDate
WHEN TransactionType = 'purchasing' THEN DeliveryDate
END
I'm trying to include a column calculated as a % of OTYPE.
IE
Order type | Status | volume of orders at each status | % of all orders at this status
SELECT
T.OTYPE,
STATUS_CD,
COUNT(STATUS_CD) AS STATVOL,
(STATVOL / COUNT(ROW_ID)) * 100
FROM Database.S_ORDER O
LEFT JOIN /* Finding definitions for status codes & attaching */
(
SELECT
ROW_ID AS TYPEJOIN,
"NAME" AS OTYPE
FROM database.S_ORDER_TYPE
) T
ON T.TYPEJOIN = ORDER_TYPE_ID
GROUP BY (T.OTYPE, STATUS_CD)
/*Excludes pending and pending online orders */
WHERE CAST(CREATED AS DATE) = '2018/09/21' AND STATUS_CD <> 'Pending'
AND STATUS_CD <> 'Pending-Online'
ORDER BY T.OTYPE, STATUS_CD DESC
OTYPE STATUS_CD STATVOL TOTALPERC
Add New Service Provisioning 2,740 100
Add New Service In-transit 13 100
Add New Service Error - Provisioning 568 100
Add New Service Error - Integration 1 100
Add New Service Complete 14,387 100
Current output just puts 100 at every line, need it to be a % of total orders
Could anyone help out a Teradata & SQL student?
The complication making this difficult is my understanding of the group by and count syntax is tenuous. It took some fiddling to get it displayed as I have it, I'm not sure how to introduce a calculated column within this combo.
Thanks in advance
There are a couple of places the total could be done, but this is the way I would do it. I also cleaned up your other sub query which was not required, and changed the date to a non-ambiguous format (change it back if it cases an issue in Teradata)
SELECT
T."NAME" as OTYPE,
STATUS_CD,
COUNT(STATUS_CD) AS STATVOL,
COUNT(STATUS_CD)*100/TotalVol as Pct
FROM database.S_ORDER O
LEFT JOIN EDWPRDR_VW40_SBLCPY.S_ORDER_TYPE T on T.ROW_ID = ORDER_TYPE_ID
cross join (select count(*) as TotalVol from database.S_ORDER) Tot
GROUP BY T."NAME", STATUS_CD, TotalVol
WHERE CAST(CREATED AS DATE) = '2018-09-21' AND STATUS_CD <> 'Pending' AND STATUS_CD <> 'Pending-Online'
ORDER BY T."NAME", STATUS_CD DESC
A where clause comes before a group by clause, so the query
shown in the question isn't valid.
Always prefix every column reference with the relevant table alias, below I have assumed that where you did not use the alias that it belongs to the orders table.
You probably do not need a subquery for this left join. While there are times when a subquery is needed or good for performance, this does not appear to be the case here.
Most modern SQL compliant databases provide "window functions", and Teradata does do this. They are extremely useful, and here when you combine count() with an over clause you can get the total of all rows without needing another subquery or join.
Because there is neither sample data nor expected result provided with the question I do not actually know which numbers you really need for your percentage calculation. Instead I have opted to show you different ways to count so that you can choose the right ones. I suspect you are getting 100 for each row because the count(status_cd) is equal to the count(row_id). You need to count status_cd differently to how you count row_id. nb: The count() function increases by 1 for every non-null value
I changed the way your date filter is applied. It is not efficient to change data on every row to suit constants in a where clause. Leave the data untouched and alter the way you apply the filter to suit the data, this is almost always more efficient (search sargable)
SELECT
t.OTYPE
, o.STATUS_CD
, COUNT(o.STATUS_CD) count_status
, COUNT(t.ROW_ID count_row_id
, count(t.row_id) over() count_row_id_over
FROM dbo.S_ORDER o
LEFT JOIN dbo.S_ORDER_TYPE t ON t.TYPEJOIN = o.ORDER_TYPE_ID
/*Excludes pending and pending online orders */
WHERE o.CREATED >= '2018-09-21' AND o.CREATED < '2018-09-22'
AND o.STATUS_CD <> 'Pending'
AND o.STATUS_CD <> 'Pending-Online'
GROUP BY
t.OTYPE
, o.STATUS_CD
ORDER BY
t.OTYPE
, o.STATUS_CD DESC
As #TomC already noted, there's no need for the join to a Derived Table. The simplest way to get the percentage is based on a Group Sum. I also changed the date to an Standard SQL Date Literal and moved the where before group by.
SELECT
t."NAME",
o.STATUS_CD,
Count(o.STATUS_CD) AS STATVOL,
-- rule of thumb: multiply first then divide, otherwise you will get unexpected results
-- (Teradata rounds after each calculation)
100.00 * STATVOL / Sum(STATVOL) Over ()
FROM database.S_ORDER AS O
/* Finding definitions for status codes & attaching */
LEFT JOIN database.S_ORDER_TYPE AS t
ON t.ROW_ID = o.ORDER_TYPE_ID
/*Excludes pending and pending online orders */
-- if o.CREATED is a Timestamp there's no need to apply the CAST
WHERE Cast(o.CREATED AS DATE) = DATE '2018-09-21'
AND o.STATUS_CD NOT IN ('Pending', 'Pending-Online')
GROUP BY (T.OTYPE, o.STATUS_CD)
ORDER BY T.OTYPE, o.STATUS_CD DESC
Btw, you probably don't need an Outer Join, Inner should return the same result.
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.
I have 2 tables UserSession and Sale.
Table User has 4 columns, namely UserID, UserSessionID, SessionOpenDate and SessionCloseDate.
Table Sale has 2 columns, which are price, cost, userID and CompletedDate.
When a user logs in, a new row is created in the User table, where the user's login timestamp will be saved in the SessionOpenDate and a new UserSessionID will be assigned to the session. When the user logs off, the log off timestamp will be be saved in SessionCloseDate.
When the user is still logged in, the user can make some sale and the sale information is saved in the Sale table. The timestamp when the sale is finalized in saved in CompletedDate column.
For some reason, I need to get the all sales done in a certain UserSessionID where the CompletedDate must be within the SessionOpenDate and SessionCloseDate. However, if the user has not logged off yet, which means that the value in SessionCloseDate is null, the CompletedDate should be between SessionOpenDate and now.
Here's my query:
SELECT SUM(s.cost) AS Cost, SUM(s.price) AS Price
FROM Sale AS s
INNER JOIN UserSession AS u
ON s.userID = u.userID
WHERE
(s.CompletedDate >=
( SELECT SessionOpenDate
FROM UserSession
WHERE (UserSessionID = u.UserSessionID)
)
)
AND
(s.CompletedDate <
(
IF EXISTS
(
SELECT SessionCloseDate AS closeTime
FROM UserSession AS UserSessionTemp
WHERE (UserSessionID = u.UserSessionID)
)
BEGIN
SET closeTime = SELECT CURRENT_TIMESTAMP
END
)
)
AND u.UserSessionID IN (1)
However, Sql Server says Incorrect syntax near the keyword 'IF'. and Incorrect syntax near ')'.
Can anyone tell me what went wrong with my IF block?
You can't use an IF block inside a SELECT statement. Also, I don't know what you're really trying to accomplish with SET, since closeTime is not a variable/parameter.
You can use IIF in SQL Server 2012 (syntactical sugar for CASE WHEN <condition> THEN <true_value> ELSE <false_value> END - use this syntax for earlier versions):
IIF(EXISTS
(
SELECT SessionCloseDate AS closeTime
FROM UserSession AS UserSessionTemp
WHERE (UserSessionID = u.UserSessionID)
), (
SELECT SessionCloseDate AS closeTime
FROM UserSession AS UserSessionTemp
WHERE (UserSessionID = u.UserSessionID)
),
CURRENT_TIMESTAMP
)
Honestly, without getting too complicated, here's what I would do instead:
SELECT SUM(s.cost) AS Cost, SUM(s.price) AS Price
FROM userSession AS u
INNER JOIN Sale AS s ON u.userID = s.userID
WHERE u.UserSessionID = #UserSessionId
AND s.CompletedDate >= u.SessionOpenDate
AND (u.SessionCloseDate IS NULL OR s.CompletedDate < u.SessionCloseDate)
Or,
SELECT SUM(s.cost) AS Cost, SUM(s.price) AS Price
FROM userSession AS u
INNER JOIN Sale AS s ON u.userID = s.userID
WHERE u.UserSessionID = #UserSessionId
AND s.CompletedDate BETWEEN u.SessionOpenDate
AND COALESCE(u.SessionCloseDate, '12/31/9999 23:59:59.9999')
I'd go with something like this, depending on what you're looking for. I couldn't tell if you wanted the info for a particular user or session, but that's simple enough to add.
SELECT UserSessionID, SUM(cost) AS TotalCost, SUM(price) AS TotalPrice
FROM UserSession LEFT OUTER JOIN sale
ON UserSession.userid = sale.userid AND
((UserSession.SessionCloseDate IS NULL AND sale.CompletedDate BETWEEN UserSession.SessionOpenDate AND GetDate())
OR (sale.SessionCloseDate IS NOT NULL AND sale.CompletedDate BETWEEN UserSession.SessionOpenDate AND UserSession.SessionCloseDate))
WHERE SUM(cost) > 0
GROUP BY UserSessionID
(you can ADD AND UserSessionID = 'mysessionid' or and UserID = 'myuserid' above the group by if you don't want the full list)
Others have explained the specific problem and "given you a fish". I would like to "teach you how to fish", though.
Please see mixed-up statement types for a full discussion (disclosure: my own blog post). Here are some snippets.
An expression consists of one or more literal values or functions tied together with operators, which when evaluated in the correct order result in a value or collection of values.
Snip...
Procedural statements are called that because there is some procedure that must be followed. It isn't a simple case of order-of-operations resulting in a single value. There is in fact no value expressed at all.
Snip...
Now that you know the three main kinds of statements (and I won't rule out the possibility of there being more or of there being subclassifications of these) the key concept you must know to get along well with SQL Server is that when a certain kind of statement is expected, you can't use a different one in its place.