Related
How can I find what was the available stock on a specific date in the past?
I know how to get OnHand for any date in the past:
SELECT LocType, LocCode, ItemCode, SUM(InQty-OutQty) [OnHand]
From OIVL
Where DocDate <= '7/7/2017'
AND ITEMCODE = 'xyz'
Group by LocType, LocCode, ItemCode
But this does not account for what was committed. It shows what was on hand, but not what was actually available (OnHand - Committed = Available). So, how could I get to this "Available" number for dates in the past?
Thanks!
In case this helps anyone else.... here is an example of how to get your item quantity and cost history in SAP BusinessOne. Pass the date and item you want to see history for into the function. If you omit item code it returns all items.
CREATE FUNCTION [dbo].[ufnStockAndCostHistory]
(
#EndDate DATETIME, #ItemCode NVARCHAR(255) = 'All'
)
RETURNS TABLE
AS
RETURN
(
WITH Cost AS (
SELECT
OINM.TransNum,
OINM.ItemCode,
OINM.Warehouse,
OINM.Balance,
OINM.CreateDate
FROM OINM (NOLOCK)
JOIN OITM (NOLOCK) ON OITM.ItemCode = OINM.ItemCode
JOIN OITB (NOLOCK) ON OITM.ItmsGrpCod=OITB.ItmsGrpCod
WHERE OINM.CreateDate <= #EndDate
),
FinalCostByWarehouse AS (
SELECT *
FROM Cost
WHERE TransNum = (
SELECT MAX(TransNum)
FROM Cost sub
WHERE Cost.ItemCode = sub.ItemCode
AND Cost.Warehouse = sub.Warehouse)
),
Quantity AS (
SELECT
OINM.ItemCode,
OINM.Warehouse,
SUM(OINM.InQty) - SUM(OINM.OutQty) [Qty]
FROM OINM (NOLOCK)
JOIN OITM (NOLOCK) ON OITM.ItemCode = OINM.ItemCode
JOIN OITB (NOLOCK) ON OITM.ItmsGrpCod=OITB.ItmsGrpCod
WHERE OINM.CreateDate <= #EndDate
group by OINM.ItemCode, OINM.Warehouse
)
SELECT
OITW.ItemCode,
OITW.WhsCode,
c.Balance [Cost],
q.Qty [Quantity]
FROM OITW
LEFT JOIN FinalCostByWarehouse c ON OITW.ItemCode = c.ItemCode
AND OITW.WhsCode = c.Warehouse
LEFT JOIN Quantity q ON OITW.ItemCode = q.ItemCode
AND OITW.WhsCode = q.Warehouse
WHERE (OITW.ItemCode = #ItemCode
OR #ItemCode = 'All')
AND (ISNULL(c.Balance,0) > 0
OR ISNULL(q.Qty,0) > 0)
)
Usage would look like:
SELECT *
FROM ufnStockAndCostHistory('9/15/2017','B107A-12HB')
I've created a TSQL query that pulls from two sets of tables in my database. The tables in the Common Table Expression are different from the tables in the main query. I'm joining on MRN and need the end result to contain accounts from both sets of tables. I've written the following query to this end:
with cteHosp as(
select Distinct p.EncounterNumber, p.MRN, p.AdmitAge
from HospitalPatients p
inner join Eligibility e on p.MRN = e.MRN
inner join HospChgDtl c on p.pt_id = c.pt_id
inner join HospitalDiagnoses d on p.pt_id = d.pt_id
where p.AdmitAge >=12
and d.dx_cd in ('G89.4','R52.1','R52.2','Z00.129')
)
Select Distinct a.AccountNo, a.dob, DATEDIFF(yy, a.dob, GETDATE()) as Age
from RHCCPTDetail c
inner join RHCAppointments a on c.ClaimID = a.ClaimID
inner join Eligibility e on c.hl7Id = e.MRN
full outer join cteHosp on e.MRN = cteHosp.MRN
where DATEDIFF(yy, a.dob, getdate()) >= 12
and left(c.PriDiag,7) in ('G89.4','R52.1','R52.2', 'Z00.129')
or (
DATEDIFF(yy, a.dob, getdate()) >= 12
and LEFT(c.DiagCode2,7) in ('G89.4','R52.1','R52.2','Z00.129')
)
or (
DATEDIFF(yy, a.dob, getdate()) >= 12
and LEFT(c.DiagCode3,7) in ('G89.4','R52.1','R52.2','Z00.129')
)
or (
DATEDIFF(yy, a.dob, getdate()) >= 12
and LEFT(c.DiagCode4,7) in ('G89.4','R52.1','R52.2','Z00.129')
)
order by AccountNo
How do I merge together the output of both the common table expression and the main query into one set of results?
Merge performs inserts, updates or deletes. I believe you want to join the cte. If so, here is an example.
Notice the cteBatch is joined to the Main query below.
with
cteBatch (BatchID,BatchDate,Creator,LogID)
as
(
select
BatchID
,dateadd(day,right(BatchID,3) -1,
cast(cast(left(BatchID,4) as varchar(4))
+ '-01-01' as date)) BatchDate
,Creator
,LogID
from tblPriceMatrixBatch b
unpivot
(
LogID
for Logs in (LogIDISI,LogIDTG,LogIDWeb)
)u
)
Select
0 as isCurrent
,i.InterfaceID
,i.InterfaceName
,b.BatchID
,b.BatchDate
,case when isdate(l.start) = 0 and isdate(l.[end]) = 0 then 'Scheduled'
when isdate(l.start) = 1 and isdate(l.[end]) = 0 then 'Running'
when isdate(l.start) = 1 and isdate(l.[end]) = 1 and isnull(l.haserror,0) = 1 then 'Failed'
when isdate(l.start) = 1 and isdate(l.[end]) = 1 and isnull(l.haserror,0) != 1 then 'Success'
else 'idunno' end as stat
,l.Start as StartTime
,l.[end] as CompleteTime
,b.Creator as Usr
from EOCSupport.dbo.Interfaces i
join EOCSupport.dbo.Logs l
on i.InterfaceID = l.InterfaceID
join cteBatch b
on b.logid = l.LogID
We have a slow query something like this:
select id
from task t
inner join TaskLog tl
on t.id = tl.id
where tl.PostImage =
(
select top 1 tll.PostImage
from TaskLog tll
where tll.id = t.id
)
group by t1.PostImage, t.id
It looked like the tl.PostImage = (subquery) caused the slowness. How do I rewrite this to make it run faster?
EDIT:
I have simplified the question in an incorrect way. Because of this the solution was not helped me. Here is the actual query I had:
SELECT distinct(p.Task_id), 1 as TotalReferrals
FROM Task p inner join Task_Log PL on PL.Task_ID = P.Task_ID
where (MONTH(P.CreateDate) = #Month
AND YEAR(P.CreateDate) = #Year)
and (((p.Worker like 'k3%'
and p.CreateDate < '12/6/2010')
or (p.Worker in ('k22', 'k27', 'k29')
and p.CreateDate >= '12/6/2010' and p.ModifiedDate < '12/1/2013')
or (p.Worker in ('K4A', 'K46', 'K48')
and p.CreateDate >= '12/1/2013'))
or (pl.post_image = (select top 1 pll.post_image
from Task_log pll
where pll.Task_id = p.Task_id
and pll.pre_image = 'Unknown'
and pll.changed_column_nm = 'Worker'
and (Month(pll.CreateDate) = #Month
and Year(pll.CreateDate) = #Year)
and ((pll.post_image like 'k3%'
and pll.CreateDate < '12/6/2010')
or (pll.post_image in ('k22', 'k27', 'k29')
and pll.CreateDate >= '12/6/2010' and pll.CreateDate < '12/1/2013')
or (pll.post_image in ('K4A', 'K46', 'K48')
and pll.CreateDate >= '12/1/2013'))
group by pll.post_image, pll.log_id
)
))
group by pl.post_image, p.Task_id, pl.CreateDate
SELECT Min(T.id)
FROM Task T
INNER JOIN TaskLog TL
ON T.id = TL.id
GROUP BY TL.PostImage
You might try to erase the subselect:
SELECT Distinct(P.Task_id), 1 as TotalReferrals
FROM Task P
INNER JOIN
Task_Log PL
ON PL.Task_ID = P.Task_ID
WHERE (MONTH(P.CreateDate) = #Month
AND YEAR(P.CreateDate) = #Year)
AND (((P.Worker like 'k3%'
AND P.CreateDate < '12/6/2010')
OR (P.Worker in ('k22', 'k27', 'k29')
AND P.CreateDate >= '12/6/2010' and P.ModifiedDate < '12/1/2013')
OR (P.Worker in ('K4A', 'K46', 'K48')
AND P.CreateDate >= '12/1/2013'))
OR (PL.pre_image = 'Unknown'
AND PL.changed_column_nm = 'Worker'
AND Month(PL.CreateDate) = #Month
AND Year(PL.CreateDate) = #Year
AND (PL.post_image like 'k3%'
AND PL.CreateDate < '12/6/2010')
OR (PL.post_image in ('k22', 'k27', 'k29')
AND PL.CreateDate >= '12/6/2010'
AND PL.CreateDate < '12/1/2013')
OR (PL.post_image in ('K4A', 'K46', 'K48')
AND PL.CreateDate >= '12/1/2013'))
GROUP BY PL.post_image, P.Task_id, PL.CreateDate
OK, let's say that I have two entities, A and B. For each user, there may be 0 or more A, with a unique combination of UserId (foreign key), GroupName, and Name properties. Entity A also has an "Value" property. Also for each user, there may be 0 or more B, with a UserID (again, a foreign key) and an "Occurred" property.
My task is to find all the B which have an Occurred property of more than 7 days ago OR have an Occurred property more than now - the number of hours in a particular A property. This code seems to work perfectly:
DateTime now = DateTime.UtcNow;
DateTime expired = now - TimeSpan.FromDays(7d);
using (DatabaseContext context = DatabaseContext.Create())
{
IQueryable<A> aQ = context.As.Where(a => a.GroupName == "Notifications" && s.Name == "Retention");
IQueryable<B> bQ = context.Bs.Where(
n => aQ.Any(a => a.UserId == b.UserId) ?
b.Occurred < EntityFunctions.AddHours(now, -aQ.FirstOrDefault(a => a.UserId == b.UserId).Value) :
b.Occurred < expired);
IList<B> bs = bQ.ToList();
// ...
}
This produces a SQL query along these lines:
SELECT
[Extent1].[ID] AS [ID],
[Extent1].[OCCURRED] AS [OCCURRED],
[Extent1].[USERID] AS [USERID],
FROM [dbo].[B] AS [Extent1]
OUTER APPLY (SELECT TOP (1)
[Extent2].[GROUPNAME] AS [GROUPNAME],
[Extent2].[NAME] AS [NAME],
[Extent2].[USERID] AS [USERID],
[Extent2].[VALUE] AS [VALUE],
FROM [dbo].[A] AS [Extent2]
WHERE (N'Notifications' = [Extent2].[GROUPNAME]) AND (N'Retention' = [Extent2].[NAME]) AND ([Extent2].[USERID] = [Extent1].[USERID]) ) AS [Element1]
OUTER APPLY (SELECT TOP (1)
[Extent3].[GROUPNAME] AS [GROUPNAME],
[Extent3].[NAME] AS [NAME],
[Extent3].[USERID] AS [USERID],
[Extent3].[VALUE] AS [VALUE],
FROM [dbo].[A] AS [Extent3]
WHERE (N'Notifications' = [Extent3].[GROUPNAME]) AND (N'Retention' = [Extent3].[NAME]) AND ([Extent3].[USERID] = [Extent1].[USERID]) ) AS [Element2]
WHERE (CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[A] AS [Extent4]
WHERE (N'Notifications' = [Extent4].[GROUPNAME]) AND (N'Retention' = [Extent4].[NAME]) AND ([Extent4].[USERID] = [Extent1].[USERID])
)) THEN CASE WHEN ( CAST( [Extent1].[OCCURRED] AS datetime2) < (DATEADD (hours, -([Element1].[VALUE]), #p__linq__0))) THEN cast(1 as bit) WHEN ( NOT ( CAST( [Extent1].[OCCURRED] AS datetime2) < (DATEADD (hours, -([Element2].[VALUE]), #p__linq__0)))) THEN cast(0 as bit) END WHEN ([Extent1].[OCCURRED] < #p__linq__1) THEN cast(1 as bit) WHEN ( NOT ([Extent1].[OCCURRED] < #p__linq__1)) THEN cast(0 as bit) END) = 1
Please note that I've hacked the code and SQL query down from the actual stuff, so this may not be the perfect representation. But I hope it gets the point across: this looks a little hairy, at least in the way that the query is repeatedly checking A for matching groupname, name, and user. But I'm no Database expert. What I do know is that one observed execution ran roughly 2.5 seconds.
Is there a better way of going about this?
Thanks for any input!
---- SOLUTION ----
Thanks to Gert for this.
The code's query ended up like this:
var bQ =
from b in context.Bs
let offset = aQ.FirstOrDefault(a => a.UserId == b.UserId)
let expiration = (null != offset) ? EntityFunctions.AddHours(now, -offset.Value) : expired
where b.Occurred < expiration
select b;
Which is almost exactly what Gert suggested. The new SQL query looks like this:
SELECT
[Extent1].[ID] AS [ID],
[Extent1].[OCCURRED] AS [OCCURRED],
[Extent1].[USERID] AS [USERID]
FROM [dbo].[B] AS [Extent1]
OUTER APPLY (SELECT TOP (1)
[Extent2].[GROUPNAME] AS [GROUPNAME],
[Extent2].[NAME] AS [NAME],
[Extent2].[USERID] AS [USERID],
[Extent2].[VALUE] AS [VALUE]
FROM [dbo].[A] AS [Extent2]
WHERE (N'Notifications' = [Extent2].[GROUPNAME]) AND (N'Retention' = [Extent2].[NAME]) AND ([Extent2].[USERID] = [Extent1].[USERID]) ) AS [Element1]
WHERE CAST( [Extent1].[OCCURRED] AS datetime2) < (CASE WHEN ([Element1].[ID] IS NOT NULL) THEN DATEADD (hour, -([Element1].[VALUE]), #p__linq__0) ELSE #p__linq__1 END)
I think that this will query the A table only once:
from b in context.Bs
let offset = aQ.FirstOrDefault(a => a.UserId == b.UserId).Value
let dd = offset.HasValue
? EntityFunctions.AddHours(now, -offset)
: expired
where b.Occurred < dd
I have select, iside select have 2 column. This column must be filled from same select, but I don't want use select twice for it. Is it possoble use select 1 time and after that set second column value from first?
Example:
insert into #temptable from
select
a = (select aa from table1 where quantity > 5)
b = (select aa from table1 where quantity > 5)
I need:
insert into #temptable from
select
a = (select aa from table1 where quantity > 5)
b = {value from a}
Update. I wrote bad example, I need set to BalancePrediction1 and BalancePrediction2 value from Balance
INSERT #tmpBalances
SELECT PA.ContractId AS 'ContractId',
Con.Name AS 'ContractName',
Bal.PortfolioAccountId AS 'PortfolioAccountId',
PA.Name AS 'PortfolioAccountName',
RA.GeneralId AS 'RegisterAccountGeneralId',
Bal.BalanceTypeId AS 'BalanceTypeId',
Bt.Name AS 'BalanceTypeName',
Bt.Type AS 'BalanceTypeType',
Bal.BalanceTimeType AS 'BalanceTimeType',
Bal.InstrumentId AS 'InstrumentId',
Ins.Name AS 'InstrumentName',
Ins.GeneralId AS 'InstrumentGeneralId',
(Bal.Balance -
(
SELECT COALESCE(SUM(Mov.Amount), 0)
FROM trd.Movements AS Mov
WHERE
Bal.InstrumentId = Mov.InstrumentId AND
Bal.PortfolioAccountId = Mov.PortfolioAccountId AND
Bal.BalanceTypeId = Mov.BalanceTypeId AND
Bal.BalanceTimeType = Mov.BalanceTimeType AND
DateDiff(DAY, #Date, Mov.Date) > 0 AND
-- Currency může být null a NULL = NULL nejde
COALESCE(Bal.CurrencyId, -1) = COALESCE(Mov.CurrencyId, -1)
)
) as Balance,
Balance AS 'BalancePrediction1',
Balance AS 'BalancePrediction2',
Bal.CurrencyId AS 'CurrencyId',
Ccy.Code AS 'CurrencyCode',
PA.PositionServiceType 'PositionServiceType',
Ccy.Name 'CurrencyName',
S.Nominal AS 'Nominal',
S.NominalCurrencyId AS 'NominalCurrencyId',
trd.GetCurrencyCode(S.NominalCurrencyId) AS 'NominalCurrencyCode'
FROM trd.Balances AS Bal
JOIN trd.PortfolioAccounts AS PA ON PA.Id = Bal.PortfolioAccountId
JOIN trd.Contracts AS Con ON Con.Id = PA.ContractId
JOIN trd.RegisterAccounts AS RA ON RA.Id = PA.RegisterAccountId
JOIN trd.BalanceTypes AS Bt ON Bt.Id = Bal.BalanceTypeId
JOIN trd.Instruments AS Ins ON Ins.Id = Bal.InstrumentId
LEFT OUTER JOIN trd.Currencies AS Ccy ON Ccy.Id = Bal.CurrencyId
LEFT JOIN trd.SecuritiesView S ON s.Id = Ins.Id AND DateDiff(d, S.ValidFrom, #Date) >= 0 AND (S.ValidTo IS NULL OR DateDiff(d, S.ValidTo, #Date) < 0)
AND S.InstrumentInstrumentTypePriceUnit = 1
You could do an update to the table variable after the insert.
update #tmpBalances
set BalancePrediction1 = Balance,
BalancePrediction2 = Balance
Or you can use cross apply to calculate the sum.
INSERT #tmpBalances
SELECT PA.ContractId AS 'ContractId',
Con.Name AS 'ContractName',
Bal.PortfolioAccountId AS 'PortfolioAccountId',
PA.Name AS 'PortfolioAccountName',
RA.GeneralId AS 'RegisterAccountGeneralId',
Bal.BalanceTypeId AS 'BalanceTypeId',
Bt.Name AS 'BalanceTypeName',
Bt.Type AS 'BalanceTypeType',
Bal.BalanceTimeType AS 'BalanceTimeType',
Bal.InstrumentId AS 'InstrumentId',
Ins.Name AS 'InstrumentName',
Ins.GeneralId AS 'InstrumentGeneralId',
(Bal.Balance - Mov.SumAmount) AS Balance,
(Bal.Balance - Mov.SumAmount) AS 'BalancePrediction1',
(Bal.Balance - Mov.SumAmount) AS 'BalancePrediction2',
Bal.CurrencyId AS 'CurrencyId',
Ccy.Code AS 'CurrencyCode',
PA.PositionServiceType 'PositionServiceType',
Ccy.Name 'CurrencyName',
S.Nominal AS 'Nominal',
S.NominalCurrencyId AS 'NominalCurrencyId',
trd.GetCurrencyCode(S.NominalCurrencyId) AS 'NominalCurrencyCode'
FROM trd.Balances AS Bal
JOIN trd.PortfolioAccounts AS PA ON PA.Id = Bal.PortfolioAccountId
JOIN trd.Contracts AS Con ON Con.Id = PA.ContractId
JOIN trd.RegisterAccounts AS RA ON RA.Id = PA.RegisterAccountId
JOIN trd.BalanceTypes AS Bt ON Bt.Id = Bal.BalanceTypeId
JOIN trd.Instruments AS Ins ON Ins.Id = Bal.InstrumentId
LEFT OUTER JOIN trd.Currencies AS Ccy ON Ccy.Id = Bal.CurrencyId
LEFT JOIN trd.SecuritiesView S ON s.Id = Ins.Id AND DateDiff(d, S.ValidFrom, #Date) >= 0 AND (S.ValidTo IS NULL OR DateDiff(d, S.ValidTo, #Date) < 0)
AND S.InstrumentInstrumentTypePriceUnit = 1
CROSS APPLY (SELECT COALESCE(SUM(Mov.Amount), 0)
FROM trd.Movements AS Mov
WHERE
Bal.InstrumentId = Mov.InstrumentId AND
Bal.PortfolioAccountId = Mov.PortfolioAccountId AND
Bal.BalanceTypeId = Mov.BalanceTypeId AND
Bal.BalanceTimeType = Mov.BalanceTimeType AND
DateDiff(DAY, #Date, Mov.Date) > 0 AND
-- Currency může být null a NULL = NULL nejde
COALESCE(Bal.CurrencyId, -1) = COALESCE(Mov.CurrencyId, -1)
) Mov(SumAmount)
SELECT aa AS a, aa AS b
FROM table1
WHERE quantity > 5
One way;
;with T (value) as (
select aa from table1 where quantity > 5
)
insert into #temptable
select value, value from T