What I had tried to do was…
for any text in column ‘phase’ where it is not ‘Pros’ or ‘SM’ than do the following:
calculate the date difference (in number of months) between today’s date and ‘estart’ date. If the number is over 36 than make it equal 36.
For text in the column ‘phase’ that are ‘Pros’ or ‘SM’ than make the value equal to the value already in column ‘col1’
name the new column ‘col12’
SELECT pbs, phase, project_name,scoping_start,col1
,CASE
WHEN phase <> 'Pros' OR phase <> 'SM' THEN
(CASE WHEN (DATEDIFF(m, GETDATE() ,MIN(estart)))>36 THEN 36 ELSE (CASE WHEN (DATEDIFF(m, GETDATE() ,MIN(estart))))
ELSE col1 END AS col12
FROM scheduledates
GROUP BY pbs, phase, project_name,scoping_start,col1
Related
I have a table (that is recreated as part of a query) with the following columns:
ID, Period (which is the number of Months, as an Integer), Payment date, Purchased Date.
I want to create a 5th column in the process: New
The calculation for which would be:
If the Payment date is within X Months of the Purchase date (where X is the number of Months) then 1.
I'm sure that this will be case expression - but I'm not sure how to do the calculation part in the case expression.
Do you just want a case expression? Something like this:
select t.*,
(case when purchasedate >= paymentdate and
purchasedate < dateadd(month, X, paymentdate)
then 1 else 0
end)
from t;
I'm trying to sum the all credits that occur before a debit, then sum all the debits after credit within a 4 day time period.
Table
ACCT |Date | Amount | Credit or debit
-----+----------+---------+----------------
152 |8/14/2017 | 48 | C
152 |8/12/2017 | 22.5 | D
152 |8/12/2017 | 40 | D
152 |8/11/2017 | 226.03 | C
152 |8/10/2017 | 143 | D
152 |8/10/2017 | 107.23 | C
152 |8/10/2017 | 20 | D
152 |8/10/2017 | 49.41 | C
My query should only sum if there is credit before the debit. the results will have 3 rows with the data above.
Output needed:
acct DateRange credit_amount debit_amount
--------------------------------------------------------------------------
152 2017-10-14 to 2017-10-18 49.41 20
152 2017-10-14 to 2017-10-18 107.23 143
152 2017-10-14 to 2017-10-18 226.03 62.5
The last one is summing the two debits until there is a credit.
First find the first credit.
sum the credits if there are more then 1 before a debit.
then find the debit and sum together until the next credit.
I only need the case where the credit date is before the debit date. The 48 on 8/14 is ignored because there is no debit after it.
The logic is to see if the account was credited then debited after it.
My attempt
DECLARE #StartDate DATE
DECLARE #EndDate DATE
DECLARE #OverallEndDate DATE
SET #OverallEndDate = '2017-08-14'
SET #StartDate = '2017-08-10'
SET #EndDate = dateadd(dd, 4, #startDate);
WITH Dates
AS (
SELECT #StartDate AS sd, #EndDate AS ed, #OverallEndDate AS od
UNION ALL
SELECT dateadd(dd, 1, sd), DATEADD(dd, 1, ed), od
FROM Dates
WHERE od > sd
), credits
AS (
SELECT DISTINCT A.Acct, LEFT(CONVERT(VARCHAR, #StartDate, 120), 10) + 'to' + LEFT(CONVERT(VARCHAR, #EndDate, 120), 10) AS DateRange, credit_amount, debit_amount
FROM (
SELECT t1.acct, sum(amount) AS credit_amount, MAX(t1.datestart) AS c_datestart
FROM [Transactions] T1
WHERE Credit_or_debit = 'C' AND T1.Datestart BETWEEN #StartDate AND #EndDate AND T1.[acct] = '152' AND T1.Datestart <= (
SELECT MIN(D1.Datestart)
FROM [Transactions] D1
WHERE T1.acct = D1.acct AND D1.Credit_or_debit = 'D' AND D1.Datestart BETWEEN #StartDate AND #EndDate
)
GROUP BY T1.acct
) AS A
CROSS JOIN (
SELECT t2.acct, sum(amount) AS debit_amount, MAX(t2.datestart) AS c_datestart
FROM [Transactions] T2 AND T2.DBCR = 'D' AND T2.Datestart BETWEEN #StartDate AND #EndDate AND T2.[acct] = '152' AND T2.Datestart <= (
SELECT MAX(D1.Datestart)
FROM [Transactions] D1
WHERE T2.acct = D1.acct AND D1.Credit_or_debit = 'D' AND D1.Datestart BETWEEN #StartDate AND #EndDate
)
GROUP BY T2.acct
) AS B
WHERE A.acct = B.acct AND A.c_datestart <= B.d_datestart
)
SELECT *
FROM credits
OPTION (MAXRECURSION 0)
Update:
The date stored is actually date timestamped. That is how I verify whether the debit is > credit.
It should be clear now that you definitely need a column that specifies the sequential order of transactions, because otherwise you can't decide whether a debit is placed befor or after a credit when they both have the same datestart. Assuming that you have such a column (in my query I named it ID), a solution could be as follows, without recursion and also without a self-join. The problem can be solved using some of the window functions available since SQL Server 2008.
My solution processes the data in several steps that I implemented as a sequence of 2 CTEs and a final PIVOT query:
DECLARE #StartDate DATE = '20170810';
DECLARE #EndDate DATE = dateadd(dd, 4, #StartDate);
DECLARE #DateRange nvarchar(24);
SET #DateRange =
CONVERT(nvarchar(10), #StartDate, 120) + ' to '
+ CONVERT(nvarchar(10), #EndDate, 120);
WITH
blocks (acct, CD, amount, blockno, r_blockno) AS (
SELECT acct, Credit_or_debit, amount
, ROW_NUMBER() OVER (PARTITION BY acct ORDER BY ID ASC)
- ROW_NUMBER() OVER (PARTITION BY acct, Credit_or_debit ORDER BY ID ASC)
, ROW_NUMBER() OVER (PARTITION BY acct ORDER BY ID DESC)
- ROW_NUMBER() OVER (PARTITION BY acct, Credit_or_debit ORDER BY ID DESC)
FROM Transactions
WHERE datestart BETWEEN #StartDate AND #EndDate
AND Credit_or_debit IN ('C','D') -- not needed, if always true
),
blockpairs (acct, CD, amount, pairno) AS (
SELECT acct, CD, amount
, DENSE_RANK() OVER (PARTITION BY acct, CD ORDER BY blockno)
FROM blocks
WHERE (blockno > 0 OR CD = 'C') -- remove leading debits
AND (r_blockno > 0 OR CD = 'D') -- remove trailing credits
)
SELECT acct, #DateRange AS DateRange
, amt.C AS credit_amount, amt.D AS debit_amount
FROM blockpairs PIVOT (SUM(amount) FOR CD IN (C, D)) amt
ORDER BY acct, pairno;
And this is how it works:
blocks
Here, the relevant data is retrieved from the table, meaning that the date range filter is applied, and another filter on the Credit_or_debit column makes sure that only the values C and D are contained in the result (if this is the case by design in your table, then that part of the WHERE clause can be omitted). The essential part in this CTE is the difference of two rownumbers (blockno). Credits and debits are numbered separately, and their respective rownumber is subtracted from the overall row number. Within a consecutive block of debits or credits, these numbers will be the same for each record, and they will be different (higher) in later blocks of the same type. The main use if this numbering is to identify the very first block (number 0) in order to be able to exclude it from
further processing in the next step in case it's a debit block. To be able to also identify the very last block (and filter it away in the next step if it's a credit block), a similar block numbering is made in the reverse order (r_blockno). The result (which I orderd just for visualization with your sample data) will look like this:
blockpairs
In this CTE, as described before, the very first block is filtered away if it's a debit block, and the very last block is filtered away if it's a credit block. Doing this, the number of remaining blocks must be even, and the logical order of blocks must be a sequence of pairs of credit and debit blocks, each pair starting with a credit block and followed by its associated debit block. Each pair of credit/debit blocks will result in a single row in the end. To associate the credit and debit blocks correctly in the query, I give them the same number by using separate numberings per type (the n-th credit block and the n-th debit block are associated by giving them the same number n). For this numbering, I use the DENSE_RANK function, for all records in a block to obtain the same number (pairno) and make the numbering gapless. For numbrting the blocks of the same type, I reuse the the blockno field described above for ordering. The result in your example (again sorted for visualization):
The final PIVOT query
Finally, the credit_amount and debit_amount are aggregated over the respective blocks grouping by acct and pairno and then diplayed side-by-side using a PIVOT query.
Although the column pairno isn't visible, it is used for sorting the resulting records.
I have all data is in one single table and when an entry is inserted, it appends a unique ID for that account. I then have a script which performs several calculations across all entries. However what I want to do is script only distinct entries in the script so I can perform the same calculation but just on the first one it finds for that account, as the info does not change between entries. The script looks as follows
However when I try to nest a sub query, it asks for a group by or aggregate, however I don't want to group by these unique account codes, otherwise I will get 1000's of rows. I just want to aggregate all entries but perform a distinct on the account details. For example age is in every entry and therefore I just need to use 1 entry for the account and not all 10 that are in there, as I will get duplicates.
select
count ([Accountid]) as Total,
round (AVG ([AGE]),2) as AVGAGE,
SUM(CASE WHEN [AGE] BETWEEN 0 AND 4 THEN 1 ELSE 0 END) AS [0-4],
SUM(CASE WHEN [AGE] > 100 THEN 1 ELSE 0 END) AS [Over 100]
from [dbo].[table1]
If I understand your question completely, I suppose that you want something like that;
select
count ([Accountid]) as Total,
round (AVG ([AGE]),2) as AVGAGE,
SUM(CASE WHEN [AGE] BETWEEN 0 AND 4 THEN 1 ELSE 0 END) AS [0-4],
SUM(CASE WHEN [AGE] > 100 THEN 1 ELSE 0 END) AS [Over 100]
from [dbo].[table1] where Accountid NOT IN (select Accountid from [table1] group by Accountid having count(*) > 1)
Hope it helps
I'm struggling to find an effective, concise way without a loop to produce groups of active dates where activity (grant/rescind activity) is based on the Switch column which has values 0 off and 1 on and start and end dates.
TransactionDate EffectiveDate TerminationDate Switch
-------------------------------------------------------------------
2013-06-14 2013-05-29 NULL 1
2013-06-14 2013-06-05 2013-06-05 0
2013-10-03 2013-05-29 2013-05-29 0
2013-10-12 2013-05-29 NULL 1
2013-10-12 2013-06-06 2013-06-06 0
The final output should be but one row:
2013-05-29 to 2013-06-06
The output is one row because the the last two transactions were switch on for 5/29/2013 and the last switch off was for 2013-06-06, which becomes the end date for the span.
Even more, the dates should also be grouped by active spans. If there were another year record in here it would need to be on a separate row.
Can I please get some help with a query to solve this issue?
Is this what you want?
select max(case when switch = 1 then effective_date end) as on_date,
(case when max(case when switch = 1 then effective_date end) <
max(case when switch = 0 then effective_date end)
then max(case when switch = 0 then effective_date end)
end) as off_date -- if any
from t;
This gets the last date the switch is on. And then the last date after that it is off.
I have a table that holds tasks. Each task has an allotted number of hours that it's supposed to take to complete the task.
I'm storing the data in a table, like so:
declare #fromtable table (recordid int identity(1,1), orderdate date, deptid int, task varchar(500), estimatedhours int);
I also have a function that calculates the completion date of the task, based on the start date, estimated hours, and department, and some other math that computes headcount, hours available to work, etc.
dbo.fn_getCapEndDate(aStartDate,estimatedHours,deptID)
I need to generate the start and end date for each record in #fromtable. The first record will start with column orderdate as the start date for the computation, then each subsequent record will use the previous record's computedEndDate as their start date.
What I'm trying to achieve:
Here's what I have started with:
with MyCTE as
(
select mt.recordID, mt.deptID, mt.estimatedhours, mt.JobNumber, ROW_NUMBER() over (order by recordID) as RowNum,
convert(date,mt.orderdate) as computedStart,
case when mt.recordID = 1 then convert(date,dbo.fn_getCapEndDate(mt.orderdate,mt.estimatedhours,mt.deptid)) end as computedEnd
from #fromtable mt
)
select c1.*, c2.recordID,
case when c2.recordid is null then c1.computedStart else c2.computedEnd end as StartDate,
case when c2.recordid is null then c1.computedEnd else dbo.fn_getCapEndDate(c2.computedEnd,c1.estimatedhours,c1.deptid) end as computedEnd
from MyCTE c1
left join MyCTE c2 on c1.RowNum = c2.RowNum + 1;
With this, the first two columns have the correct start/end dates. Every column after that computes NULL for its start and end values. It "loses" the value of the previous column's computed end date.
What can I do to fix the issue and return the values as needed?
EDIT: Sample data in text format:
estimatedhours OrderDate
0 1/1/2017
0 1/1/2017
0 1/1/2017
0 1/1/2017
500 1/1/2017
32 1/1/2017
0 1/1/2017
0 1/1/2017
320 1/1/2017
0 1/1/2017
5 1/1/2017
0 1/1/2017
4 1/1/2017
You can use lead as below:
select RecordId, EstimatedHours, StartDate,
ComputedEnd = LEAD(StartDate) over (order by RecordId)
From yourTable