I just want to ask you guys, especially those with MsSQL knowledge, regarding my query.
My goal is to get the average delivery time and group my data by delivery date and route id daily/weekly/monthly.
Here's my query:
SELECT
RouteID,
CONVERT(date, [DeliveryDate]) AS delivery_date,
AVG(
DATEDIFF(
day,
CONVERT(date, [UnloadDate]),
CONVERT(date, [DeliveryDate])
)
) as Averate_Delivery_Time
FROM [CARGODB].[dbo].[Cargo_Transactions]
WHERE
[DeliveryDate] IS NOT NULL AND
[UnloadDate] != 0 AND
[StageID] = 'D' AND
( CONVERT(date, [DeliveryDate]) LIKE '%2016%' or
CONVERT(date, [DeliveryDate]) LIKE '%2017%')
GROUP BY CONVERT(date, [DeliveryDate]), [RouteID]
ORDER BY CONVERT(date, [DeliveryDate]) DESC
I am not confident if the average delivery time is correct so if you think it's wrong or there are other things in my query that needs to be corrected, please let me know.
UPDATE:
I was able to get the right query:
SELECT [RouteID],
CAST(DATEPART(YEAR,[DeliveryDate]) as varchar) + ' Week ' +
CAST(DATEPART(WEEK,[DeliveryDate]) AS varchar) AS week_name,
AVG(DATEDIFF(day, CONVERT(date, [UnloadDate]), CONVERT(date,
[DeliveryDate]))) as Average_Delivery_Days
FROM [CARGODB].[dbo].[Cargo_Transactions]
WHERE [DeliveryDate] IS NOT NULL AND [DeliveryDate] != 0
AND CONVERT(date, [DeliveryDate]) BETWEEN '2016-01-01' AND GETDATE()
AND [UnloadDate] IS NOT NULL AND [UnloadDate] != 0 AND [DeliveryDate] >
[UnloadDate]
AND [Deleted] = 0 and [StageID] = 'D'
GROUP BY DATEPART(YEAR,[DeliveryDate]), DATEPART(WEEK,[DeliveryDate]),
[RouteID]
ORDER BY DATEPART(YEAR,[DeliveryDate]), DATEPART(WEEK,[DeliveryDate]),
Average_Delivery_Days desc
But I have a more complicated query to do now. I have this sample data:
RouteID | week_name | yearnum | weeknum | Average_Delivery_Days
=======================================================================
MK | 2016 Week 2 | 2016 | 2 | 1
-----------------------------------------------------------------------
TSM | 2016 Week 2 | 2016 | 2 | 1
-----------------------------------------------------------------------
E | 2016 Week 2 | 2016 | 2 | 1
-----------------------------------------------------------------------
A | 2016 Week 2 | 2016 | 2 | 1
-----------------------------------------------------------------------
D | 2016 Week 2 | 2016 | 2 | 1
-----------------------------------------------------------------------
MP | 2016 Week 2 | 2016 | 2 | 1
-----------------------------------------------------------------------
CTN | 2016 Week 3 | 2016 | 3 | 9
-----------------------------------------------------------------------
BIS | 2016 Week 3 | 2016 | 3 | 8
-----------------------------------------------------------------------
C | 2016 Week 3 | 2016 | 3 | 1
-----------------------------------------------------------------------
PN | 2016 Week 4 | 2016 | 4 |10
-----------------------------------------------------------------------
How can I make the above data be like:
MK and TSM are merged into 1 new routeID like Manila1
E, A, and D are merged into another as Manila2
MP, CTN, AND BIS as Visayas
C and PN as Mindanao
and so on..
And the average delivery days will be changed as well.
Your help is highly appreciated. Thank you!
Related
I have a list of members by week and I need to compare the current week to the previous in SQL Server.The first image is how the data is in table and the second image is what I want as a result. I thought maybe doing a CTE for each week and then comparing them. Thanks.
A very naive approach that counts all mem_id values that weren't in the previous week as new could look like this:
declare #t table(mem_id int,weeknum int, yearnum int);
insert into #t values(1,1,2020),(2,1,2020),(1,2,2020),(3,2,2020),(2,3,2020),(3,3,2020),(4,3,2020);
with p as
(
select yearnum
,weeknum
,case when lag(weeknum,1) over (partition by mem_id order by yearnum,weeknum) = weeknum-1 then 0 else 1 end as p
from #t
)
select yearnum
,weeknum
,sum(p) as new
,count(1) as total
from p
group by yearnum
,weeknum
order by yearnum
,weeknum;
Output
+---------+---------+-----+-------+
| yearnum | weeknum | new | total |
+---------+---------+-----+-------+
| 2020 | 1 | 2 | 2 |
| 2020 | 2 | 1 | 2 |
| 2020 | 3 | 2 | 3 |
+---------+---------+-----+-------+
I need to sum up values from Money column for each WeekNumber.
Now I have view:
WeekNumber | DayTime | Money
---------------------------------------
1 | 2012-01-01 | 20.4
1 | 2012-01-02 | 30.5
1 | 2012-01-03 | 55.1
2 | 2012-02-01 | 67.3
2 | 2012-02-02 | 33.4
3 | 2012-03-01 | 11.8
3 | 2012-03-04 | 23.9
3 | 2012-03-05 | 34.3
4 | 2012-04-01 | 76.6
4 | 2012-04-02 | 90.3
Tsql:
SELECT datepart(week,DayTime) AS WeekNumber, DayTime, Money FROM dbo.Transactions
In conclusion, I would like to get something like this:
WeekNumber | DayTime | Sum
---------------------------------------
1 | 2012-01-01 | 106
2 | 2012-02-02 | 100.7
3 | 2012-03-03 | 470
4 | 2012-04-01 | 166.9
DayTime should be random for each Week Number but exists in column DayTime from view above.
Please, be free to write your ideas. Thanks.
SELECT datepart(week,DayTime) AS WeekNumber
, MIN(DayTime) DayTime --<-- Instead of random get first date from your data in that week
, SUM(Money) AS [Sum]
FROM dbo.Transactions
GROUP BY datepart(week,DayTime)
Try this
SELECT datepart(week,DayTime) AS WeekNumber, SUM(Money) FROM dbo.Transactions GROUP BY WeekNumber
As you will have number of rows for each week you cannot get DayTime with the same table. There are other ways to add that too like JOIN
Change your SQL to sum the money column. Like this
SELECT
datepart(week,DayTime) AS WeekNumber,
DayTime, Money = SUM(Money)
FROM dbo.Transactions
GROUP BY datepart(week,DayTime),DayTime
SELECT datepart(week, DayTime) AS WeekNumber
,MIN(DayTime)
,SUM(MONEY)
FROM dbo.Transactions
GROUP BY datepart(week, DayTime)
Let's assume I have in SQL Server the following table with only seven days available (SUN - SAT):
Orders
| Day | ProductType | Price |
| SUN | 1 | 10 |
| MON | 1 | 15 |
| MON | 2 | 20 |
| MON | 3 | 10 |
| TUE | 1 | 5 |
| TUE | 3 | 5 |
...
I need to group the data in a way so that to see the Total sum of Prices by each distinct Day and two groups of ProductType (= 1 and > 1):
| Day | FirstProductTypeTotal | RestProductsTypesTotal | GrandTotal |
| SUN | 10 | 0 | 10 |
| MON | 15 | 30 | 45 |
| TUE | 5 | 5 | 10 |
...
where FirstProductTypeTotal is ProductType = 1 and RestProductTypesTotal is ProductType > 1.
Is it possible to select this in one select instead of writing two different selects:
Select Day, SUM(Price) as FirstTotal from Orders where ProductType = 1 group by Day
and
Select Day, SUM(Price) as SecondTotal from Orders where ProductType > 1 group by Day
And then add FirstTotal and SecondTotal manually in the code to get the Grand total for each day of the week?
Use CASE Expression
Select Day, SUM(CASE WHEN ProductType = 1 THE Price ELSE 0 END) AS FirstTotal,
SUM(CASE WHEN ProductType > 1 THE Price ELSE 0 END) AS SecondTotal,
SUM(Price) AS GrandTotal
FROM Orders
group by Day
Try conditional aggregation;
Sample data;
CREATE TABLE #Orders ([Day] varchar(10), ProductType int, Price int)
INSERT INTO #Orders ([Day],ProductType, Price)
VALUES
('SUN',1,10)
,('MON',1,15)
,('MON',2,20)
,('MON',3,10)
,('TUE',1,5)
,('TUE',3,5)
Query;
SELECT
o.[Day]
,SUM(CASE WHEN o.ProductType = 1 THEN o.Price ELSE 0 END) FirstTotal
,SUM(CASE WHEN o.ProductType > 1 THEN o.Price ELSE 0 END) SecondTotal
,SUM(o.Price) GrandTotal
FROM #Orders o
GROUP BY o.[Day]
Result
Day FirstTotal SecondTotal GrandTotal
MON 15 30 45
SUN 10 0 10
TUE 5 5 10
You'd just need to sort out the ordering of the days because SQL Server by definition doesn't store the data in any particular order.
I have a table with the following format
YEAR, MONTH, ITEM, REQ_QTY1, REQ_QTY2 , ....REQ_QTY31 ,CONVERTED1, CONVERTED2 ....CONVERTED31
Where the suffix of each column is the day of the month.
I need to convert it to the following format, where Day_of_month is the numeric suffix of each column
YEAR, MONTH, DAY_OF_MONTH, ITEM, REQ_QTY, CONVERTED
I thought of using CROSS APPLY to retrieve the data, but I can't use CROSS APPLY to get the "Day of Month"
SELECT A.YEAR, A.MONTH, A.ITEM, B.REQ_QTY, B.CONVERTED
FROM TEST A
CROSS APPLY
(VALUES
(REQ_QTY1, CONVERTED1),
(REQ_QTY2, CONVERTED2),
(REQ_QTY3, CONVERTED3),
......
(REQ_QTY31, CONVERTED31)
)B (REQ_QTY, CONVERTED)
The only way I found is to use a nested select with inner join
SELECT A.YEAR, A.MONTH, A.DAY_OF_MONTH, A.ITEM,A.REQ_QTY, D.CONVERTED FROM
(SELECT YEAR, MONTH, ITEM, SUBSTRING(DAY_OF_MONTH,8,2) AS DAY_OF_MONTH, REQ_QTY FROM TEST
UNPIVOT
(REQ_QTY FOR DAY_OF_MONTH IN ([REQ_QTY1],[REQ_QTY2],[REQ_QTY3],......[REQ_QTY30],[REQ_QTY31])
) B
) A
INNER JOIN (SELECT YEAR, MONTH, ITEM, SUBSTRING(DAY_OF_MONTH,10,2) AS DAY_OF_MONTH, CONVERTED FROM TEST
UNPIVOT
(CONVERTED FOR DAY_OF_MONTH IN ([CONVERTED1],[CONVERTED2],[CONVERTED3],....[CONVERTED30],[CONVERTED31])
) C
) D
ON D.YEAR = A.YEAR AND D.MONTH = A.MONTH AND D.ITEM = A.ITEM AND D.DAY_OF_MONTH = A.DAY_OF_MONTH
Is there a way to use CROSS APPLY and yet get the DAY_OF_MONTH out?
This is not a solution with CROSS APPLY but it will definitely make it a bit faster as it uses a bit simpler approach and simpler execution plan.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE Test_Table([YEAR] INT, [MONTH] INT, [ITEM] INT, REQ_QTY1 INT
, REQ_QTY2 INT ,REQ_QTY3 INT , CONVERTED1 INT, CONVERTED2 INT, CONVERTED3 INT)
INSERT INTO Test_Table VALUES
( 2015 , 1 , 1 , 10 , 20 , 30 , 100 , 200 , 300),
( 2015 , 2 , 1 , 10 , 20 , 30 , 100 , 200 , 300),
( 2015 , 3 , 1 , 10 , 20 , 30 , 100 , 200 , 300)
Query 1:
SELECT *
FROM
(
SELECT [YEAR]
,[MONTH]
,ITEM
,Vals
,CASE WHEN LEFT(N,3) = 'REQ' THEN SUBSTRING(N,8 ,2)
WHEN LEFT(N,3) = 'CON' THEN SUBSTRING(N,10,2)
END AS Day_Of_Month
,CASE WHEN LEFT(N,3) = 'REQ' THEN LEFT(N,7)
WHEN LEFT(N,3) = 'CON' THEN LEFT(N,9)
END AS Tran_Type
FROM Test_Table t
UNPIVOT (Vals FOR N IN ([REQ_QTY1],[REQ_QTY2],[REQ_QTY3],
[CONVERTED1],[CONVERTED2],[CONVERTED3]))up
)t2
PIVOT (SUM(Vals)
FOR Tran_Type
IN (REQ_QTY, CONVERTED))p
Results:
| YEAR | MONTH | ITEM | Day_Of_Month | REQ_QTY | CONVERTED |
|------|-------|------|--------------|---------|-----------|
| 2015 | 1 | 1 | 1 | 10 | 100 |
| 2015 | 1 | 1 | 2 | 20 | 200 |
| 2015 | 1 | 1 | 3 | 30 | 300 |
| 2015 | 2 | 1 | 1 | 10 | 100 |
| 2015 | 2 | 1 | 2 | 20 | 200 |
| 2015 | 2 | 1 | 3 | 30 | 300 |
| 2015 | 3 | 1 | 1 | 10 | 100 |
| 2015 | 3 | 1 | 2 | 20 | 200 |
| 2015 | 3 | 1 | 3 | 30 | 300 |
Well, I found a way using CROSS APPLY, but instead of taking a substring, I'm basically hardcoding the days. Works well enough so...
SELECT A.YEAR, A.MONTH, A.ITEM, B.DAY_OF_MONTH, B.REQ_QTY, B.CONVERTED
FROM TEST A
CROSS APPLY
(
VALUES
('01', REQ_QTY1, CONVERTED1),
('02', REQ_QTY2, CONVERTED2),
('03', REQ_QTY3, CONVERTED3),
('04', REQ_QTY4, CONVERTED4),
......
('31', REQ_QTY31, CONVERTED31)
) B (DAY_OF_MONTH, REQ_QTY, CONVERTED)
Table contains the following columns and data:
Id | Date
1 | 04/09/2014 13:00:00
1 | 04/10/2014 15:00:00
1 | 04/12/2014 16:00:00
1 | 04/13/2014 18:00:00
2 | 04/11/2014 13:00:00
2 | 04/12/2014 15:00:00
2 | 04/12/2014 16:00:00
I need to get every vertical pair for each Id and transform it into horizontal view, the result should look like:
Id | Date1 | Date2
1 | 04/09/2014 13:00:00 04/10/2014 15:00:00
1 | 04/12/2014 16:00:00 04/13/2014 18:00:00
2 | 04/11/2014 13:00:00 04/12/2014 15:00:00
2 | 04/12/2014 16:00:00 NULL
There is a null value since date is absence in a pair of Id = 2. There are no more columns in the table.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
create table T
(
Id int,
Date datetime
)
go
insert into T values
(1 ,'04/09/2014 13:00:00'),
(1 ,'04/10/2014 15:00:00'),
(1 ,'04/12/2014 16:00:00'),
(1 ,'04/13/2014 18:00:00'),
(2 ,'04/11/2014 13:00:00'),
(2 ,'04/12/2014 15:00:00'),
(2 ,'04/12/2014 16:00:00')
Query 1:
select T.Id,
min(T.Date) as Date1,
case when count(*) = 2 then max(T.Date) end as Date2
from (
select T.Id,
T.Date,
(1 + row_number() over(partition by T.Id order by T.Date)) / 2 as rn
from T
) as T
group by T.Id, T.rn
order by T.Id, T.rn
Results:
| ID | DATE1 | DATE2 |
|----|------------------------------|------------------------------|
| 1 | April, 09 2014 13:00:00+0000 | April, 10 2014 15:00:00+0000 |
| 1 | April, 12 2014 16:00:00+0000 | April, 13 2014 18:00:00+0000 |
| 2 | April, 11 2014 13:00:00+0000 | April, 12 2014 15:00:00+0000 |
| 2 | April, 12 2014 16:00:00+0000 | (null) |
SQL Fiddle
MS SQL Server 2008 Schema Setup:
create table T
(
Id int,
Date datetime
)
go
insert into T values
(1 ,'04/09/2014 13:00:00'),
(1 ,'04/10/2014 15:00:00'),
(1 ,'04/12/2014 16:00:00'),
(1 ,'04/13/2014 18:00:00'),
(2 ,'04/11/2014 13:00:00'),
(2 ,'04/12/2014 15:00:00'),
(2 ,'04/12/2014 16:00:00')
Query 1:
select T1.Id,
T1.Date as Date1,
T3.Date2
from T as T1
outer apply (
select top(1) T2.Date
from T as T2
where T1.Date < T2.Date and
T1.Id = T2.Id
order by T2.Date
) as T3(Date2)
Results:
| ID | DATE1 | DATE2 |
|----|------------------------------|------------------------------|
| 1 | April, 09 2014 13:00:00+0000 | April, 10 2014 15:00:00+0000 |
| 1 | April, 10 2014 15:00:00+0000 | April, 12 2014 16:00:00+0000 |
| 1 | April, 12 2014 16:00:00+0000 | April, 13 2014 18:00:00+0000 |
| 1 | April, 13 2014 18:00:00+0000 | (null) |
| 2 | April, 11 2014 13:00:00+0000 | April, 12 2014 15:00:00+0000 |
| 2 | April, 12 2014 15:00:00+0000 | April, 12 2014 16:00:00+0000 |
| 2 | April, 12 2014 16:00:00+0000 | (null) |