How can I do subtraction between two rows based on their id value? - tsql

I have the following table basically hour reading of the equipment based on the shifts. It contained over 1500 rows. What I want to do is subtract the next shifts reading from the previous one so that I can find the hours worked for that equipment.
Id Shift Eqpmt HourReading
-- ----- ----- ------------
1 Shift1 E21 2488
2 Shift1 E52 36882
3 Shift1 Q53 2384
4 Shift1 S54 44874
. . . .
. . . .
11 Shift2 E21 2500
12 Shift2 E52 36900
13 Shift2 Q53 2388
14 Shift2 S54 44875
. . . .
. . . .
. . . .
select
distinct sh.Shift
,sh.Eqmpt
,(a.HourReading-sh.HourReading) WorkedHrs
from sh
join (select id,Shift, Eqpmt, HourReading from sh) a on
a.Eqpmt=sh.Eqpmt and a.id>sh.id
I tried the above script but it subtracts every shift's value and giving me over 30000 records. Actually, I added myself that id column to do that operation but it seems it's not working still.
and this is what I want to get actually
Shift Eqpmt WorkedHours
------ ----- ------------
Shift1 E21 12
Shift1 E52 8
Shift1 Q53 4
Shift1 S54 1

I think your attempted query would work for this sample data, but I take it you have more than 2 shifts in the actual data (e.g. "Shift3", "Shift4", etc). Therefore I think you might want to look at using lead.
Something like:
SELECT *
FROM
(
SELECT shift, eqpmt, lead(hourreading) OVER (PARTITION BY eqpmt ORDER BY id) - hourreading as workedhours
FROM sh
) a
WHERE workedhours is not null;
The lead(hourreading) OVER (PARTITION BY eqpmt ORDER BY id) bit will get the next row (ordered by id) that has the same eqpmt value. I wrapped it in the outer query checking for workedhours is not null so that the last shift (that isn't finished yet) doesn't show up in the result.

Related

Running Count Total with PostgresQL

I'm fairly close to this solution, but I just need a little help getting over the end.
I'm trying to get a running count of the occurrences of client_ids regardless of the date, however I need the dates and ids to still appear in my results to verify everything.
I found part of the solution here but have not been able to modify it enough for my needs.
Here is what the answer should be, counting if the occurrences of the client_ids sequentially :
id client_id deliver_on running_total
1 138 2017-10-01 1
2 29 2017-10-01 1
3 138 2017-10-01 2
4 29 2013-10-02 2
5 29 2013-10-02 3
6 29 2013-10-03 4
7 138 2013-10-03 3
However, here is what I'm getting:
id client_id deliver_on running_total
1 138 2017-10-01 1
2 29 2017-10-01 1
3 138 2017-10-01 1
4 29 2013-10-02 3
5 29 2013-10-02 3
6 29 2013-10-03 1
7 138 2013-10-03 2
Rather than counting the times the client_id appears sequentially, the code counts the time the id appears in the previous date range.
Here is my code and any help would be greatly appreciated.
Thank you,
SELECT n.id, n.client_id, n.deliver_on, COUNT(n.client_id) AS "running_total"
FROM orders n
LEFT JOIN orders o
ON (o.client_id = n.client_id
AND n.deliver_on > o.deliver_on)
GROUP BY n.id, n.deliver_on, n.client_id
ORDER BY n.deliver_on ASC
* EDIT WITH ANSWER *
I ending up solving my own question. Here is the solution with comments:
-- Set "1" for counting to be used later
WITH DATA AS (
SELECT
orders.id,
orders.client_id,
orders.deliver_on,
COUNT(1) -- Creates a column of "1" for counting the occurrences
FROM orders
GROUP BY 1
ORDER BY deliver_on, client_id
)
SELECT
id,
client_id,
deliver_on,
SUM(COUNT) OVER (PARTITION BY client_id
ORDER BY client_id, deliver_on
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) -- Counts the sequential client_ids based on the number of times they appear
FROM DATA
Just the answer posted to close the question:
-- Set "1" for counting to be used later
WITH DATA AS (
SELECT
orders.id,
orders.client_id,
orders.deliver_on,
COUNT(1) -- Creates a column of "1" for counting the occurrences
FROM orders
GROUP BY 1
ORDER BY deliver_on, client_id
)
SELECT
id,
client_id,
deliver_on,
SUM(COUNT) OVER (PARTITION BY client_id
ORDER BY client_id, deliver_on
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) -- Counts the sequential client_ids based on the number of times they appear
FROM DATA

How do I merge two result sets without matching results getting multiplied?

I have a query that returns a list of items and blank serial number fields like this:
Item S/N
1
1
1
2
3
3
3
3
. . .
The duplication is intended, as the number of rows for an item corresponds to the number of items purchased. I also have a table that contains items that have already received serial numbers:
Item S/N
2 416
3 72
3 319
. . .
The query to build the first list does not reference the table with the serial numbers.
Now, I want to modify the result of the query with the information in the table
such that serial numbers from the table get added to the matching items. But, where there are not enough serial numbers, I want the blank serial numbers preserved (note that the table will never have more entries for an item than the query). The new result would look like this:
Item S/N
1
1
1
2 416
3 72
3 319
3
3
. . .
I thought a left outer join would give me what I want, using my query results on the left (via a common table expression) and my table on the right. But when I tried it, this is what I got:
Item S/N
1
1
1
2 416
3 72
3 72
3 72
3 72
3 319
3 319
3 319
3 319
. . .
It looks like the query performs a cross join on matching items, but preserves left side row cardinality when there is no match; not what I want. Is there a join or method I can use to get what I want?
You can do this using row_number():
select t1.item, t2.s_n
from (select t1.*,
row_number() over (partition by item order by item) as seqnum
from t1
) t1 left join
(select t2.*
row_number() over (partition by item order by item) as seqnum
from t2
) t2
on t1.item = t2.item and t1.seqnum = t2.seqnum;

TSQL Join to get all records from table A for each record in table B?

I have two tables:
PeriodId Period (Periods Table)
-------- -------
1 Week 1
2 Week 2
3 Week 3
EmpId PeriodId ApprovedDate (Worked Table)
----- -------- ------------
1 1 Null
1 2 2/28/2013
2 2 2/28/2013
I am trying to write a query that results in this:
EmpId Period Worked ApprovedDate
----- -------- --------- ------------
1 Week 1 Yes Null
1 Week 2 Yes 2/28/2013
1 Week 3 No Null
2 Week 1 No Null
2 Week 2 Yes 2/28/2013
2 Week 3 No Null
The idea is that I need each Period from the Periods table for each Emp. If there was no record in the Worked table then the 'No' value is placed Worked field.
What does the TSQL look like to get this result?
(Note: if it helps I also have access to an Employee table that has EmpId and LastName for each employee. For performance reasons I'm hoping not to need this but if I do then so be it.)
You should be able to use the following:
select p.empid,
p.period,
case
when w.PeriodId is not null
then 'Yes'
else 'No' End Worked,
w.ApprovedDate
from
(
select p.periodid, p.period, e.empid
from periods p
cross join (select distinct EmpId from worked) e
) p
left join worked w
on p.periodid = w.periodid
and p.empid = w.empid
order by p.empid
See SQL Fiddle with Demo

SELECT record based upon dates

Assuming data such as the following:
ID EffDate Rate
1 12/12/2011 100
1 01/01/2012 110
1 02/01/2012 120
2 01/01/2012 40
2 02/01/2012 50
3 01/01/2012 25
3 03/01/2012 30
3 05/01/2012 35
How would I find the rate for ID 2 as of 1/15/2012?
Or, the rate for ID 1 for 1/15/2012?
In other words, how do I do a query that finds the correct rate when the date falls between the EffDate for two records? (Rate should be for the date prior to the selected date).
Thanks,
John
How about this:
SELECT Rate
FROM Table1
WHERE ID = 1 AND EffDate = (
SELECT MAX(EffDate)
FROM Table1
WHERE ID = 1 AND EffDate <= '2012-15-01');
Here's an SQL Fiddle to play with. I assume here that 'ID/EffDate' pair is unique for all table (at least the opposite doesn't make sense).
SELECT TOP 1 Rate FROM the_table
WHERE ID=whatever AND EffDate <='whatever'
ORDER BY EffDate DESC
if I read you right.
(edited to suit my idea of ms-sql which I have no idea about).

need help writing a date sensitive T-SQL query

I need help writing a T-SQL query that will generate 52 rows of data per franchise from a table that will often contain gaps in the 52 week sequence per franchise (i.e., the franchise may have reported data bi-weekly or has not been in business for a full year).
The table I'm querying against looks something like this:
FranchiseId | Date | ContractHours | PrivateHours
and I need to join it to a table similar to this:
FranchiseId | Name
The output of the query needs to look like this:
Name | Date | ContractHours | PrivateHours
---- ---------- ------------- ------------
AZ1 08-02-2011 292 897
AZ1 07-26-2011 0 0 -- default to 0's for gaps in sequence
...
AZ1 08-03-2010 45 125 -- row 52 for AZ1
AZ2 08-02-2011 382 239
...
AZ2 07-26-2011 0 0 -- row 52 for AZ2
I need this style of output for every franchise, i.e., 52 rows of data with default rows for any gaps in the 52 week sequence, in a single result set. Thus, if there are 100 franchises, the result set should be 5200 rows.
What I've Tried
I've tried the typical suggestions of:
Create a table with all possible dates
LEFT OUTER JOIN this to the table of data needed
The problems I'm running into are
ensuring that for every franchise their are 52 rows and
filling in gaps with the franchise name and 0 for hours, I can't
have the following in the result set:
Name | Date | ContractHours | PrivateHours
---- ---------- ------------- ------------
NULL 08-02-2011 NULL NULL
I don't know where to go from here? Is there an efficient way to write a T-SQL query that will produce the required output?
The bare bones is this
Generate 52 week ranges
Cross join with Franchise
LEFT JOIN the actual date
ISNULL to substitute zeroes
So, like this, untested
;WITH cDATE AS
(
SELECT
CAST('20100101' AS date /*smalldatetime*/) AS StartOfWeek,
CAST('20100101' AS date /*smalldatetime*/) + 6 AS EndOfWeek
UNION ALL
SELECT StartOfWeek + 7, EndOfWeek + 7
FROM cDATE WHERE StartOfWeek + 7 < '20110101'
), Possibles AS
(
SELECT
StartOfWeek, FranchiseID
FROM
cDATE CROSS JOIN Franchise
)
SELECT
P.FranchiseID,
P.StartOfWeek,
ISNULL(SUM(O.ContractHours), 0),
ISNULL(SUM(O.PrivateHours), 0)
FROM
Possibles P
LEFT JOIN
TheOtherTable O ON P.FranchiseID = O.FranchiseID AND
O.Date BETWEEN P.StartOfWeek AND P.EndOfWeek
GROUP BY
P.FranchiseID