Joining three tables with OVER, for accumulative monthly figures - sql-server

I have made the amendments as per comments and entered here as not enough characters allowed in comments. I think there may be a few issues on the OVER and with the JOIN. Updated query:
SELECT RIGHT('0' + CAST(day(oh_datetime) AS VARCHAR (2)), 2),
SUM(SUM((CASE oh_sot_id WHEN 1 THEN 1 WHEN 4 THEN -1 END) * oht_net)) over (ORDER BY day(oh_datetime) ROWS UNBOUNDED PRECEDING) AS 'Orders In($)',
SUM((CASE oh_cd_id WHEN 11728 THEN 1 END) * oht_net) AS 'Target($)',
SUM(SUM((CASE ih_credit WHEN 'false' THEN 1 WHEN 'true' THEN -1 END) * ih_net)) over (ORDER BY day(ih_datetime) ROWS UNBOUNDED PRECEDING) AS 'Sales($)'
FROM order_header_total
JOIN order_header ON order_header_total.oht_oh_id = order_header.oh_id
JOIN invoice_header ON order_header.oh_id = invoice_header.ih_oh_id
WHERE oh_datetime >= DATEADD(day, 1, EOMONTH(GETDATE(), -1)) AND oh_datetime < DATEADD(day, 1, EOMONTH(GETDATE()))
GROUP BY day(oh_datetime), day(ih_datetime)
UNION SELECT 'YAxis','0','0','0'
Table should display:
Date
Orders In
Target
Sales
01
6402.19
12321.57
128539.13
02
17795.94
24643.14
148258.63
03
50703.09
36964.71
171231.14
04
116034.29
49286.28
188157.69
28
353989.36
345004.00
446808.05
I have not done the full month for sake of space, but included the last day to show how it should accumulate. Instead it looks like this:
Date
Orders In
Target
Sales
01
5507.32
5507.32
01
5833.52
5833.52
01
6402.19
29377.18
02
10312.35
188157.69
02
16592.09
16023.42
02
17795.94
54950.68
03
30439.28
28666.76
03
30581.03
28808.51
04
36029.72
24700.77
04
37082.36
38767.55
23
144191.70
143992.45
Again a selection of data and then showing the last row for the sake of space. The date is not grouping, 'Orders In' and 'Sales' are not accumulating, and there is no data displaying for 'Target'.

Related

SQL join combined with new column calculation

I have two tables in SQL that look like this:
Table 1:
ID TaxYear Earnings
01 2000 2234
01 2001 123
02 2004 12344
02 2006 234
02 2007 0
02 2008 123
Table 2:
ID JobEnd
01 1998
02 2000
02 2007
I need to combine these tables to make a new column giving the number of years between TaxYear and JobEnd. However, I need this value to reset every time TaxYear passes a new JobEnd year. So my final table would look like this:
ID TaxYear Earnings YearsSinceJobEnd
01 2000 2234 2
01 2001 123 3
02 2004 12344 4
02 2006 234 6
02 2007 0 7
02 2008 123 1
For ID 02, when YearsSinceJobEnd is calculated as TaxYear minus 2000, up until TaxYear passes the new JobEnd year of 2007, when it is subsequently calculated as TaxYear minus 2007.
I'm getting very confused about how to do this. If I join the tables I end up with multiple columns per TaxYear, which I need to avoid. But I can't think how to calculate the new column without joining them.
Any help would be much appreciated.
You can use datediff() with DATETIMEFROMPARTS ():
select t1.id, t1.TaxYear, t1.Earnings,
datediff(year, DATEFROMPARTS(t2.JobEnd, 1, 1), DATEFROMPARTS(t1.TaxYear, 1, 1)) as YearsSinceJobEnd
from t1 inner join
t2
on t2.id = t1.id;
If you don't want JOIN then use APPLY :
select t1.id, t1.TaxYear, t1.Earnings,
datediff(year, DATEFROMPARTS(t2.JobEnd, 1, 1), DATEFROMPARTS(t1.TaxYear, 1, 1)) as YearsSinceJobEnd
from t1 cross apply
( select top (1) t2.JobEnd
from t2
where t2.id = t1.id and t2.JobEnd < t1.TaxYear
order by t2.JobEnd desc
) t2;
you can user CROSS APPLY to find the required JobEnd for each ID
SELECT t1.ID, t1.TaxYear, t1.Earnings,
YearsSinceJobEnd = t1.TaxYear - e.JobEnd
FROM Table1 t1
CROSS APPLY
(
SELECT JobEnd = MAX(t2.JobEnd)
FROM Table2 t2
WHERE t2.ID = t1.ID
AND t2.JobEnd < t1.TaxYear
) e

How to join tables when there are duplicates in right table

I have three tables. Table Cust has a custID field, plus various other values (name, address etc)
Table List has a single column ID. Each ID is a custID in the Cust table
Edit: the purpose of this is to filter the records, restricting thge results to ones where the CustID appears in the list table.
All three tables are indexed.
Table Trans has a TransactionID field, a Cust field that holds a customer ID, And other transaction fields
Edit: I should have mentioned that in some cases there will be no transaction record. In this case I want one row of Customer info with the transaction fields null or blank.
I want a query to return cust and transaction ID for each ID in the list table. If there is more than one matching row in the transaction table, I want each included along 3with the matching cust info. So if the tables look like This:
Cust
ID Name
01 John
02 Mary
03 Mike
04 Jane
05 Sue
06 Frank
List
ID
01
03
05
06
Transact
TransID CustId Msg
21 01 There
22 01 is
23 02 a
24 03 tide
25 04 in
26 04 the
27 05 affairs
28 05 of
29 05 men
I want the result set to be:
CustID Name TransID Msg
01 John 21 There
01 John 22 is
03 Mike 24 tide
05 Sue 27 affairs
05 Sue 28 of
05 Sue 29 men
06 Frank -- --
(Where -- represents NULL or BLANK)
Obviously the actual tables are much larger (millions of rows), but that shows the pattern, one row for every item in table Transactions that matches any of the items in the List table, with matching fields from the Cust table. if there is no matching Transaction, one row of customer info from each ID in the List table. CustID is unique in the Cust and List tables, but not in the transaction table.
This needs to work on any version of SQL server from 2005 onward, if that matters.
Any suggestions?
Unless I'm missing something, this is all you need to do:
Select T.CustID, C.Name, T.TransID, T.Msg
From Transact T
Join Cust C On C.Id = T.CustId
Join List L On L.Id = C.Id
Order By T.CustID, T.TransID
;with cust (id, name) as
(
select 1, 'John' union all
select 2, 'Mary' union all
select 3, 'Mike' union all
select 4, 'Jane' union all
select 5, 'Sue'
), list (id) as
(
select 1 union all
select 3 union all
select 5
), transact (TransId, CustId, Msg) as
(
select 21, 1, 'There '
union all select 22, 1, 'is'
union all select 23, 2, 'a'
union all select 24, 3, 'tide'
union all select 25, 4, 'in'
union all select 26, 4, 'the'
union all select 27, 5, 'affairs'
union all select 28, 5, 'of'
union all select 29, 5, 'men'
)
select
CustId = c.id,
Name = c.Name,
TransId = t.TransId,
Msg = t.Msg
from cust c
inner join list l
on c.id = l.id
inner join transact t
on l.id = t.custid
yields:
CustId Name TransId Msg
----------- ---- ----------- -------
1 John 21 There
1 John 22 is
3 Mike 24 tide
5 Sue 27 affairs
5 Sue 28 of
5 Sue 29 men

Sql comparaison nested on one column

Imagine a table :
ID Month Year Value 1
1 May 17 58
2 June 09 42
3 December 18 58
4 December 18 58
5 September 10 84
6 May 17 42
7 January 16 3
I want to return all the data that shares the same month and year where Value 1 is different. So in our example, I want to return 1 and 6 only but not 3 and 4 or any of the other entries.
Is there a way to do this? I am thinking about a combination of distinct and group by but can't seem to come up with the right answer being new to SQL.
Thanks.
It could be done without grouping, but with simple self-join:
select distinct t1.*
from [Table] t1
inner join [Table] t2 on
t1.Month = t2.Month
and t1.Year = t2.Year
and t1.Value_1 <> t2.Value_1
You can find some information and self-join examples here and here.
For each row you can examine aggregates in its group with the OVER clause. eg:
create table #t(id int, month varchar(20), year int, value int)
insert into #t(id,month,year,value)
values
(1,'May' ,17, 58 ),
(2,'June' ,09, 42 ),
(3,'December' ,18, 58 ),
(4,'December' ,18, 58 ),
(5,'September',10, 84 ),
(6,'May' ,17, 42 ),
(7,'January' ,16, 3 );
with q as
(
select *,
min(value) over (partition by month,year) value_min,
max(value) over (partition by month,year) value_max
from #t
)
select id,month,year,value
from q
where value_min <> value_max;
If I understood your question correctly, you are looking for the HAVING keyword.
If you GROUP BY Month, Year, Value_1 HAVING COUNT(*) = 1, you get all combinations of Month, Year and Value_1 that have no other occurrence.

Summing every third record grouped by three fields

I have a database where the travel times are segmented by epochs and are done on a 5 minute bin, that I need to summation up to 15 minutes. This is tricky because the data is segment by a TMC value, a date, and epoch. None of which are unique. As such:
TMC DATE EPOCH Travel_TIME_ALL_VEHICLES
111N20176 7012015 64 63
111N20176 7012015 76 112
111N20176 7012015 80 114
111N20176 7012015 83 127
111N20176 7012015 91 58
111N20176 7012015 93 117
I need the first three travel times to be added together, then the next three, and then the next three, and so on.
select *, sum(Travel_TIME_ALL_VEHICLES) over (order by EPOCH rows between 3 preceding and current row) as rolling_avg
from [dbo].[FHWA_2015_weekend]
WHERE TMC = '113P12373' order by DATE, EPOCH
In MySQL (as the question was originally tagged), you can enumerate the data using variables and then do the aggregation.
For your sample data this should work:
select tmc, date, sum(Travel_TIME_ALL_VEHICLES)
from (select t.*, (#rn := #rn + 1) as rn
from t cross join
(select #rn := 0) params
order by date, epoch
) t
group by tmc, date, floor((#rn - 1) / 3);
In almost any other database, you would use row_number() for this purpose:
select tmc, date, sum(Travel_TIME_ALL_VEHICLES)
from (select t.*, row_number() over (order by date, epoch) as rn
from t
) t
group by tmc, date, floor((rn - 1) / 3);

sql query to calculate the time difference between the two successive rows

Can anyone help me to calculate the time difference(in hh:mm) between the two rows, when the Procs_ID changes from 01 to another Procs_ID.
Procs_ID meter_id date
01 0000012 2015-10-12 09:07:22.530
03 0000013 2015-10-12 09:11:51.733
01 0000014 2015-10-12 09:12:38.550
02 0000015 2015-10-12 10:38:52.923
03 0000016 2015-10-12 10:40:33.467
01 0000017 2015-10-12 10:40:56.013
Thanks in advance
Here is an example with LEAD which is avaliable for SQL Server 2012+
select
*,
case
when Procs_Id = '01'
and lead(Procs_ID) over (order by [date]) <> Procs_ID then datediff(mi,[date],lead([date]) over (order by [date]))
end as TimeDiffInMin
from YourTable

Resources