I have a table in SQL Server that looks something like this
ProjectId BookedHours FiscalYear FiscalMonth
--------------------------------------------------
PRJ1 2040 2015-16 1-Apr-15
PRJ1 1816 2015-16 1-May-15
PRJ1 1760 2015-16 1-Jun-15
PRJ1 1832 2015-16 1-Jul-15
PRJ2 1752 2015-16 1-Sep-15
PRJ2 1529 2015-16 1-Oct-15
PRJ2 1336 2015-16 1-Nov-15
PRJ2 1480 2015-16 1-Dec-15
PRJ2 522 2015-16 1-Jan-16
I need to sum up the values for current + previous two months booked hours i.e. expected result should look like below table
ProjectId BookedHours FiscalYear FiscalMonth ExpectedValue
-----------------------------------------------------------------
PRJ1 2040 2015-16 1-Apr-15 2040
PRJ1 1816 2015-16 1-May-15 3856
PRJ1 1760 2015-16 1-Jun-15 5616
PRJ1 1832 2015-16 1-Jul-15 5408
PRJ2 1752 2015-16 1-Sep-15 1752
PRJ2 1529 2015-16 1-Oct-15 3281
PRJ2 1336 2015-16 1-Nov-15 4617
PRJ2 1480 2015-16 1-Dec-15 4345
PRJ2 522 2015-16 1-Jan-16 3338
This is one approach...
WITH cte AS
(
SELECT
row_num = ROW_NUMBER() OVER(ORDER BY FiscalMonth),
*
FROM dbo.Project p
)
SELECT CurrentMonth.ProjectID, CurrentMonth.BookedHours, CurrentMonth.FiscalYear, CurrentMonth.FiscalMonth,
(CurrentMonth.BookedHours + COALESCE(OneMonthBack.BookedHours, 0) + COALESCE(TwoMonths.BookedHours, 0)) AS ExpectedValue
FROM cte CurrentMonth
LEFT JOIN cte OneMonthBack ON OneMonthBack.row_num = CurrentMonth.row_num - 1
LEFT JOIN cte TwoMonths ON TwoMonths.row_num = CurrentMonth.row_num - 2
Hope that works for you.
WITH cte AS
(
SELECT *,row_num=ROW_NUMBER() OVER( PARTITION BY Projectid ORDER BY Projectid,FiscalYear,FiscalMonth)
FROM dbo.Project p
)
SELECT CM.ProjectID, CM.FiscalYear, CM.FiscalMonth, CM.BookedHours,
(CM.BookedHours + COALESCE(OMB.BookedHours, 0) + COALESCE(TM.BookedHours, 0)) AS ExpectedValue
FROM cte CM
LEFT OUTER JOIN cte OMB WITH(NOLOCK) ON OMB.row_num = CM.row_num - 1 and CM.Projectid=OMB.Projectid
LEFT OUTER JOIN cte TM WITH(NOLOCK) ON TM.row_num = CM.row_num - 2 and CM.Projectid=TM.Projectid
ORDER BY CM.ProjectID, CM.FiscalYear, CM.FiscMonth ASC
Above Query works perfectly for my table
Related
I have these two tables
ItemMov
Item Date
A 2018-03-31
A 2018-03-30
A 2018-03-25
B 2018-03-28
B 2018-03-29
and DateTable
Date
2018-03-31
2018-03-30
2018-03-29
2018-03-28
2018-03-27
2018-03-26
2018-03-25
What i need is to have a sort of cross product between those two but limited to the earlest date for each item.
Initially i used simple cross product.
select distinct t.Item, f.Date from ItemMov t cross join DateTable f
getting this
Item Date
A 2018-03-31
A 2018-03-30
A 2018-03-29
A 2018-03-28
A 2018-03-27
A 2018-03-26
A 2018-03-25
B 2018-03-31
B 2018-03-30
B 2018-03-29
B 2018-03-28
B 2018-03-27
B 2018-03-26
B 2018-03-25
Of course this crossing brings dates early than the first date for some items, like B.
The desired output should be something like that but limited to the min(date) for each item. So, is there a why of doing this with crossing tables or should a use some sort of loop?
Desired output
Item Date
A 2018-03-31
A 2018-03-30
A 2018-03-29
A 2018-03-28
A 2018-03-27
A 2018-03-26
A 2018-03-25
B 2018-03-31
B 2018-03-30
B 2018-03-29
B 2018-03-28
Consider a join with on clause date expression:
select distinct t.Item, f.Date
from ItemMov t
inner join DateTable f on t.Date <= f.Date
-- cross join DateTable f on t.Date <= f.Date
order by t.Item, f.Date desc
Rextester demo
You can aggregate before doing a join:
select i.item, d.date
from (select i.item, min(date) as min_date, max(date) as max_date
from itemmov i
group by i.item
) i join
datetable d
on d.date >= i.min_date and d.date <= i.max_date
order by i.item, d.date;
i'm trying to get a sum of a column for each month. this is what i've done.
My SQL Query :
SELECT `ID_Pilot` ,`PilotStationDate` ,`PilotWaitingTime`, ( SELECT FORMAT(SUM( `PilotWaitingTime` ),2) FROM pilot ) AS Total
FROM pilot
this is the result :
ID_Pilot PilotStationDate PilotWaitingTime Total
P001 2013-01-01 0.2 39.20
P002 2013-01-02 19.2 39.20
P003 2013-01-03 7.8 39.20
P004 2013-02-04 6.4 39.20
P005 2013-02-06 5.6 39.20
and this is the result i want :
ID_Pilot PilotStationDate PilotWaitingTime Total
P001 2013-01-01 0.2 27.20
P002 2013-01-02 19.2 27.20
P003 2013-01-03 7.8 27.20
P004 2013-02-04 6.4 12.00
P005 2013-02-06 5.6 12.00
note: 27.20 in Total column is a sum of PilotWaitingTime in January and 12.00 is a sum of PilotWaitingTime in February
Thank you for your help
This query will return the sum for each month:
SELECT
DATE_FORMAT(PilotStationDate, '%Y-%m') as year_month,
FORMAT(SUM( `PilotWaitingTime` ),2) as total
FROM pilot
GROUP BY
DATE_FORMAT(PilotStationDate, '%Y-%m')
then you just can join back this query with the pilot table:
SELECT
p.`ID_Pilot`,
p.`PilotStationDate`,
p.`PilotWaitingTime`,
t.total
FROM
pilot p INNER JOIN (
SELECT
DATE_FORMAT(PilotStationDate, '%Y-%m') as year_month,
FORMAT(SUM( `PilotWaitingTime` ),2) as total
FROM pilot
GROUP BY
DATE_FORMAT(PilotStationDate, '%Y-%m')
) t ON DATE_FORMAT(p.PilotStationDate, '%Y-%m') = t.year_month
Your pilot table data would be helpful to provide the perfect query. But you can try these two queries.
SELECT `ID_Pilot` ,`PilotStationDate` ,`PilotWaitingTime`, ( SELECT FORMAT(SUM( `PilotWaitingTime` ),2) FROM pilot p2 where p2.ID_Pilot = p1.ID_Pilot ) AS Total
FROM pilot p1;
OR
SELECT `ID_Pilot` ,`PilotStationDate` ,`PilotWaitingTime`, FORMAT(SUM( `PilotWaitingTime`),2) AS Total
FROM pilot GROUP BY ID_Pilot;
Here's the query you could try out (Example http://sqlfiddle.com/#!9/96667/1)
select pilot.*, totals.tot
from pilot
inner join
(
select
year(pilotstationdate) as yr,
month(pilotstationdate) as mth,
sum(pilotwaitingtime) as tot
from pilot
group by
year(pilotstationdate),
month(pilotstationdate)
) totals
on year(pilot.pilotstationdate) = totals.yr
and month(pilot.pilotstationdate) = totals.mth
select p.id_pilot, p.pilotstationdate, p.pilotwaitingtime,
(select sum(pilotwaitingtime)
from pilot
where last_day(pilotstationdate)=last_day(p.pilotstationdate)) total
from pilot p
last_day() is a mysql function that returns the last day of the month.
Having a structure of 3 tables
Table min consist of matcode,min_qty,jo_no,mr_no
Table min_out_body consist of matcode,out_qty,jo_no,mr_no
Table eu_min_out_body consist of matcode,out_qty,jo_no,mr_no
And data as follow:
[min]
matcode min_qty jo_no mr_no
xxx 100 1A A11
xxx 150 2A A22
[min_out_body]
matcode out_qty jo_no mr_no
xxx 10 1A A11
xxx 60 1A A11
xxx 100 2A A22
[eu_min_out_body]
matcode out_qty jo_no mr_no
xxx 20 1A A11
xxx 50 2A A22
What i am trying to achieve is to have a result:
matcode min_qty jo_no mr_no balance
xxx 100 1A A11 10
xxx 150 2A A22 0
Queried using following code :
SELECT
min.matcode,
min.min_qty,
min.jo_no,
min.mr_no
(min.min_qty-(
select ifnull(sum(out_qty),0)
FROM min_out_body
WHERE matcode=min.matcode
and jo_no=min.jo_no
and mr_no=min.mr_no
)-(
select ifnull(sum(out_qty),0)
FROM eu_min_out_body
WHERE matcode=min.matcode
and jo_no=min.jo_no
and mr_no=min.mr_no
)
) as balance
FROM min
WHERE min.matcode = 'xxx'
and (min.min_qty - (select
ifnull(sum(out_qty),0)
FROM min_out_body
WHERE matcode = min.matcode
and jo_no = min.jo_no
and mr_no = min.mr_no) - (select
ifnull(sum(out_qty),0)
FROM eu_min_out_body
WHERE matcode = min.matcode
and jo_no = min.jo_no
and mr_no = min.mr_no)) > 0
I can get the result, but is there any way to simplify the query and reduce the process time?
I see two options, and depending on the amount of data you are dealing with could perform better than other... The first is easier readability wise but simplifies your WHERE clause and may be what you want to run with..
Wrap your query but only up to the WHERE MAT_CODE qualifier. Let that "PQ" (PreQuery) return everything -- ALL balances. Then from that, just apply WHERE balance > 0 vs your complex balance calculation completed AGAIN in the where clause.
SELECT PQ.*
from
( SELECT
min.matcode,
min.min_qty,
min.jo_no,
min.mr_no
(min.min_qty
- ( select ifnull(sum(out_qty),0)
FROM min_out_body
WHERE min_no = min.min_no
and matcode = min.matcode
and jo_no = min.jo_no
and mr_no = min.mr_no )
- ( select ifnull(sum(out_qty),0)
FROM eu_min_out_body
WHERE min_no=min.min_no
and matcode=min.matcode
and jo_no=min.jo_no
and mr_no=min.mr_no ) ) as balance
FROM
min
WHERE
min.matcode = 'xxx' ) PQ
where
balance > 0
If a larger data set, It looks a little larger since it does pre-aggregate queries ONCE per the "out_body" tables and groups by the components your inner field selects were dealing with. Then LEFT JOIN to those tables. The benefit of this approach is that it doesn't run the inner select sum() on a per record basis, and if you have 1000's of records to go through, might be a bigger time saver for you... especially if its going through MIN_OUT_BODY and EU_MIN_OUT_BODY.
By Left-Join, it's either their or not, and the IFNULL() works the same way, then the WHERE clause is the same simple readable computation of the balance WITHOUT having to requery yet again.
SELECT
min.matcode,
min.min_qty,
min.jo_no,
min.mr_no,
min.min_qty - ifnull( MOB1.out_qty, 0 ) - ifnull( EMOB1.out_qty, 0 ) balance
from
min
LEFT JOIN ( select
mob.matcode,
mob.min_no,
mob.jo_no,
mob.mr_no,
SUM( mob.out_qty ) out_qty
from
min_out_body mob
where
mob.matcode = 'xxx'
group by
mob.matcode,
mob.min_no,
mob.jo_no,
mob.mr_no ) MOB1
ON min.matcode = MOB1.matcode
AND min.min_no = MOB1.min_no
AND min.jo_no = MOB1.jo_no
AND min.mr_no = MOB1.mr_no
LEFT JOIN ( select
Emob.matcode,
Emob.min_no,
Emob.jo_no,
Emob.mr_no,
SUM( Emob.out_qty ) out_qty
from
eu_min_out_body Emob
where
Emob.matcode = 'xxx'
group by
Emob.matcode,
Emob.min_no,
Emob.jo_no,
Emob.mr_no ) EMOB1
ON min.matcode = EMOB1.matcode
AND min.min_no = EMOB1.min_no
AND min.jo_no = EMOB1.jo_no
AND min.mr_no = EMOB1.mr_no
where
min.matcode = 'xxx'
AND min.min_qty - ifnull( MOB1.out_qty, 0 ) - ifnull( EMOB1.out_qty, 0 ) > 0
I have two tables in MS Access 2010:
PART LOG-OUT TABLE:
PART_ID DRAWN_DATE LOCATION_ID
C0001 07/29/2013 501
C0002 07/29/2013 604
C0003 08/01/2013 703
C0004 08/01/2013 807
C0005 08/02/2013 505
C0006 08/02/2013 602
C0007 08/02/2013 707
C0008 08/03/2013 802
C0009 08/03/2013 803
C0001 10/01/2013 605
C0002 10/02/2013 704
C0004 10/05/2013 806
PART RETURN TABLE:
PART_ID RETURN_DATE LOCATION_ID
C0001 09/04/2013 STORE
C0002 09/05/2013 STORE
C0004 09/10/2013 STORE
C0007 09/12/2013 STORE
C0008 09/13/2013 STORE
C0002 10/03/2013 STORE
This is the result I want:
PART_ID DRAWN_DATE LOG-OUT LOCATION RETURN_DATE RETURN LOCATION
C0001 07/29/2013 501 09/04/2013 STORE
C0001 10/01/2013 605
C0002 07/29/2013 604 09/05/2013 STORE
C0002 10/02/2013 704 10/03/2013 STORE
C0003 08/01/2013 703
C0004 08/01/2013 807 09/10/2013 STORE
C0004 10/05/2013 806
C0005 08/02/2013 505
C0006 08/02/2013 602
C0007 08/02/2013 707 09/13/2013 STORE
C0008 08/03/2013 802 10/03/2013 STORE
C0009 08/03/2013 803
But I can only get this:
PART_ID DRAWN_DATE LOG-OUT LOCATION RETURN_DATE RETURN LOCATION
C0001 07/29/2013 501 09/04/2013 STORE
C0001 10/01/2013 605 `09/04/2013 STORE`
C0002 07/29/2013 604 09/05/2013 STORE
`C0002 07/29/2013 604` 10/03/2013 STORE
C0002 10/02/2013 704 `09/05/2013 STORE`
`C0002 10/02/2013 704 10/03/2013 STORE`
C0003 08/01/2013 703
C0004 08/01/2013 807 09/10/2013 STORE
C0004 10/05/2013 806 `09/10/2013 STORE`
C0005 08/02/2013 505
C0006 08/02/2013 602
C0007 08/02/2013 707 09/13/2013 STORE
C0008 08/03/2013 802 10/03/2013 STORE
C0009 08/03/2013 803
after I wrote:
SELECT L.PART_ID, L.DRAWN_DATE, L.LOCATION_ID AS [LOG-OUT LOCATION], R.RETURN_DATE, R.LOCATION_ID AS RETURN_LOCATION FROM (SELECT * FROM [PART LOG-OUT] ORDER BY PART_ID) AS L LEFT JOIN (SELECT * FROM [PART RETURN] ORDER BY PART_ID) AS R ON L.PART_ID = R.PART_ID ORDER BY L.PART_ID, L.DRAWN_DATE, R.RETURN_DATE;
Can somebody correct me? Thanks!
Your requirements are a bit vague so I may be off, but you seem to want to pair returns with times that are before the return only, something like this?
SELECT L.PART_ID, L.DRAWN_DATE, L.LOCATION_ID AS [LOG-OUT LOCATION],
MIN(R.RETURN_DATE), MIN(R.LOCATION_ID) AS RETURN_LOCATION
FROM (SELECT * FROM [LOG_OUT LOCATION]) AS L
LEFT JOIN (SELECT * FROM [PART_RETURN]) AS R
ON L.PART_ID = R.PART_ID AND L.DRAWN_DATE < R.RETURN_DATE
GROUP BY L.LOCATION_ID,L.PART_ID,L.DRAWN_DATE
ORDER BY L.PART_ID, L.DRAWN_DATE, MIN(R.RETURN_DATE)
An SQLfiddle to test with.
Note that since there's nothing pairing a single purchase with a single return (and no such sample in your question), the logic is very basic for pairing them up.
As you are joining on the PART_ID column, the result you are getting is what you would expect to get.
For example, there are two C0001 in the LOG-OUT table and these will join to the same C0001 in the PART RETURN table as there is nothing to distringuish the rows in the return table from each other:
PART LOG-OUT TABLE -> PART RETURN TABLE
C0001 07/29/2013 501 -> C0001 09/04/2013 STORE
C0001 10/01/2013 605 -> C0001 09/04/2013 STORE
You need another criteria to join on, or you need to join on some more unique identifier in order to achieve the results you are looking for.
SELECT L.PART_ID, L.DRAWN_DATE, L.LOCATION_ID AS [LOG-OUT LOCATION], R.RETURN_DATE, R.LOCATION_ID AS RETURN_LOCATION FROM (SELECT * FROM [PART LOG-OUT] ORDER BY PART_ID) AS L LEFT OUTER JOIN (SELECT * FROM [PART RETURN] ORDER BY PART_ID) AS R ON L.PART_ID = R.PART_ID ORDER BY L.PART_ID, L.DRAWN_DATE, R.RETURN_DATE;
I need to refine this sql select:
SELECT ch_num, payment_type, Total FROM transactions WHERE ( ch_num )
IN (
SELECT ch_num FROM transactions GROUP BY ch_num HAVING count('ch_num') > 1
)
ORDER BY `ch_num`
Result i get is:
ch_num payment_type Total
20001 visa 36.60
20001 visa 36.60
20001 mc 30.60
50019 cash 9.00
50019 mc 18.95
50023 cash 2.70
50023 visa 7.00
But i need results rows only where there is 'cash' payment_type so 'ch_no' 20001 should be omited.
Correct result would be then:
ch_num payment_type Total
50019 cash 9.00
50019 mc 18.95
50023 cash 2.70
50023 visa 7.00
SELECT ch_num, payment_type, Total
FROM transactions
WHERE ch_num IN
(
SELECT ch_num
FROM transactions
GROUP BY ch_no
HAVING count('ch_num') > 1
and sum(payment_type='cash') >= 1
)
ORDER BY `ch_num`
Here's a complete, proven code example, with test results at the end. I used Oracle, but syntax should be the same for the SQL SELECT.
create table transactions (ch_num int, payment_type varchar2(100), total float);
insert into transactions values(20001,'visa',36.60);
insert into transactions values(20001,'mc',30.60);
insert into transactions values(50019,'cash',9.00);
insert into transactions values(50019,'mc',18.95);
insert into transactions values(50023,'cash',2.70);
insert into transactions values(50023,'visa',7.00);
SELECT ch_num, payment_type, Total FROM transactions a WHERE ( ch_num )
IN (
SELECT ch_num FROM transactions GROUP BY ch_num HAVING count(ch_num) > 1
)
AND EXISTS
(SELECT ch_num FROM transactions b where payment_type = 'cash' and a.ch_num = b.ch_num)
ORDER BY ch_num
Results:
CH_NUM PAYMENT_TYPE TOTAL
1 50019 cash 9
2 50019 mc 18.95
3 50023 cash 2.7
4 50023 visa 7