Join tables by Column name - sql-server

I've got a table with vehicles mark and sales date. I need to take a query to take how many different vehicles has been sold in this year, separated by months, for example:
| January | February | March | April |..............
----------------------------------------------------------
mark1 | | 1 | 5 | |..............
mark2 | 45 | | | 7 |..............
mark3 | 12 | 11 | 5 | 3 |..............
The original table is:
mark | soldDate
----------------------------
mark1 | 01/07/2020
mark2 | 04/07/2020
mark1 | 05/07/2020
mark3 | 06/07/2020
If i want to take how many different vehicles has been sold i use this query:
SELECT mark, COUNT(mark) WHERE FORMAT(soldDate, 'MMMM') = 'january' GROUP BY mark
How can i divide the data in every single month?

With conditional aggregation:
select mark,
count(case when month(soldDate) = 1 then 1 end) as January,
count(case when month(soldDate) = 2 then 1 end) as February,
...........................................................
where year(soldDate) = 2020
group by mark

SELECT Mark, DATENAME(MONTH, DATEADD(MONTH, MONTH(SalesDate) - 1, '1900-01-01')) M, COUNT(*) COUNT
FROM VehicleSales
WHERE YEAR(SalesDate) = '2020'
GROUP BY Mark, MONTH(SalesDate)
Order by Mark, M

Related

Compare current week to previous week

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 |
+---------+---------+-----+-------+

SQL Server GROUP BY multiple columns

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.

how to get data from different hours at current date in ms sql

how to use select statement in MS-SQL to get data from different hours at current date?
I have a table that has 3 columns: Id, name, dateFinished; the name column will add by different time period.
For example:
//assume today is 17/05/2016
Id= 1, name= A, dateFinished = '17/05/2016 08:00';
Id= 2, name= A, dateFinished = '17/05/2016 10:00';
Id= 3, name= A, dateFinished = '17/05/2016 12:00';
Id= 4, name= B, dateFinished = '17/05/2016 09:00';
Id= 5, name= C, dateFinished = '17/05/2016 10:00';
Id= 6, name= B, dateFinished = '17/05/2016 11:00';
//and the records will keep update by name persons when the time pass.
According to the table above, if we check the range between 08:00 to 10:00. We can see the Name A person has two finished times, and B person and C person only has one finished time.
My point is how to count the amount of the person's dateFinished time in different time range at current date?
//question updated
my desired output table will be:
name-------08AM-10AM(total Finished)-------10AM-12PM
A-------2-------1
B-------1-------1
C-------1-------0
and the query must use current date method(ex. GETDATE()), not input string!
If my question is duplicate, please mark as duplicated question and give the related ref.
Thanks a lot!
SQL Fiddle Demo
SELECT [name], COUNT([name]) as Total
FROM Table1
WHERE dateFinished BETWEEN '05/17/2016 08:00' AND '05/17/2016 10:00'
GROUP BY [name]
OUTPUT
| name | Total |
|------|-------|
| A | 2 |
| B | 1 |
| C | 1 |
Not sure I understand exactly what you're after -- something that looks like this maybe?
Name | Date | 8am | 9am | 10am | 11am | 12pm
A | 17/05/2016 | 1 | 0 | 1 | 0 | 1
B | 17/05/2016 | 0 | 1 | 0 | 1 | 0
C | 17/05/2016 | 0 | 0 | 1 | 0 | 0
...
Here is a way to get that:
SELECT
myName,
myDate,
[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23]
FROM
(
SELECT
ID,
Name as myName,
Convert(varchar,date_finished,112) as myDate,
DATEPART(HOUR, date_finished) as myHour
FROM
Production.Product
) AS SourceTable
PIVOT
(
COUNT(ID)
FOR myHour IN ([0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23])
) AS PivotTable;
select
name,
convert(nvarchar(12),
dateFinished,101) AS [DATE],
count(name) AS TOTAL
from
**TABLE NAME**
group by
name,
convert(nvarchar(12),dateFinished,101)
OUTPUT
| name | date | total
| A | 05/17/2016| 2
| B | 05/17/2016| 1

Complex SQL query, not sure where to start

I have a tough one here I think. I have the following tables:
[Assets]
AssetId | Name
1 | Acura NSX
2 | Dodge Ram
[Assignments]
AssignmentId | AssetId | StartMileage | EndMileage | StartDate | EndDate
1 | 1 | 8000 | 10000 | 4/1/2015 | 5/1/2015
2 | 1 | 10000 | 16000 | 9/15/2015 | 1/5/2016
3 | 2 | 51000 | NULL | 1/1/2016 | NULL
[Reminders]
ReminderId | AssetId | Name | Distance | Time | Active
1 | 1 | Oil Change | 3000 (miles)| 3 (months)| 1
2 | 1 | Tire Rotation | 5000 | 6 | 0
3 | 2 | Oil Change | 3000 | 3 | 1
4 | 2 | Air Filter | 50000 | 48 | 1
[Maintenance]
MaintenanceId | AssetId | ReminderId | Mileage | Date | Vendor
1 | 1 | 1 | 10000 | 5/1/2015 | Jiffy Lube
2 | 2 | 3 | 51000 | 6/1/2015 | Dealership
I need a query that will join these 4 tables and return something like the following.
Name | Name | Current Mileage | Last Mileage | Last Date
Acura NSX | Oil Change | 16000 | 10000 | 5/1/2015
Dodge RAM | Oil Change | 51000 | 51000 | 6/1/2015
Dodge RAM | Air Filter | 51000 | -- | --
I need to take the distance threshold from the Reminders table and add it to the mileage from the Maintenance table then compare it to the start and end mileage from the Assignments table. If the threshold is greater than the start or end mileage then select the asset name, the name of the reminder, the current mileage (start or end mileage from Assignments, whichever is greater), and mileage and date from the last maintenance for that reminder. I need to do the same for time threshold. Add it to the date from the Maintenance table then compare it to today's date. If it's greater then display the asset.
Can one of you SQL gurus help me with this please?
UPDATE:
SELECT
v.Name,
r.Name AS Reminder,
a.CurrentMileage,
i.MaintenanceMileage,
i.MaintenanceDate
FROM
Assets v
LEFT JOIN
(SELECT AssetId,
COALESCE(EndMileage, StartMileage) AS CurrentMileage,
ROW_NUMBER() OVER (PARTITION BY AssetId
ORDER BY AssignmentId DESC) AS window_id
FROM Assignments) a
ON v.AssetId = a.AssetId
AND a.window_id = 1
JOIN
Reminders r
ON v.AssetId = r.AssetId
AND r.ActiveFlag = 1
LEFT JOIN
(SELECT AssetId,
ReminderId,
MAX(Mileage) AS MaintenanceMileage,
MAX([Date]) AS MaintenanceDate
FROM Maintenances
GROUP BY AssetId, ReminderId) i
ON r.ReminderId = i.ReminderId
AND (a.CurrentMileage > (NULLIF(i.MaintenanceMileage, 0) + r.DistanceThreshold))
OR (GETDATE() > DATEADD(m, r.[TimeThreshold], i.MaintenanceDate))
Here is a starting point:
SELECT v.Name AS [Asset Name], r.Name AS Reminder, a.CurrentMileage,
m.Mileage + r.Distance AS [Last Mileage], m.[Date] AS [Last Date]
FROM Assets v
JOIN ( -- get the latest relevant row as window_id = 1
SELECT AssetId, COALESCE(EndMileage, StartMileage) AS CurrentMileage,
COALESCE(EndDate, StartDate) AS AssignDate,
ROW_NUMBER() OVER (partition by AssetId
order by COALESCE(EndDate, StartDate) DESC) AS window_id
FROM Assignments
) a
ON v.AssetId = a.AssetId
AND a.window_id = 1
JOIN Reminders r
ON v.AssetId = r.AssetId
AND r.Active = 1
LEFT JOIN Maintenance m
ON r.AssetId = m.AssetId
AND r.ReminderId = m.ReminderId
-- corrected
AND ((a.CurrentMileage > (NULLIF(m.Mileage, 0) + r.Distance))
-- slightly oversimplified
OR (GETDATE() > DATEADD(m, r.[Time], COALESCE(m.[Date], a.AssignDate))))
The date calculations are slightly oversimplified because they use the latest assignment dates. What you would really want is a column Assets.InServiceDate that would anchor the time before the first maintenance would be due. But this will get you started.

SQL Query for Date Range, multiple start/end times

A table exists in Microsoft SQL Server with record ID, Start Date, End Date and Quantity.
The idea is that for each record, the quantity/total days in range = daily quantity.
Given that a table containing all possible dates exists, how can I generate a result set in SQL Server to look like the following example?
EX:
RecordID | Start Date | End Date | Quantity
1 | 1/1/2010 | 1/5/2010 | 30000
2 | 1/3/2010 | 1/9/2010 | 20000
3 | 1/1/2010 | 1/7/2010 | 10000
Results as
1 | 1/1/2010 | QTY (I can do the math easy, just need the dates view)
1 | 1/2/2010 |
1 | 1/3/2010 |
1 | 1/4/2010 |
1 | 1/3/2010 |
2 | 1/4/2010 |
2 | 1/5/2010 |
2 | 1/6/2010 |
2 | 1/7/2010 |
2 | 1/8/2010 |
2 | 1/9/2010 |
3 | 1/1/2010 |
3 | 1/2/2010 |
3 | 1/3/2010 |
3 | 1/4/2010 |
3 | 1/5/2010 |
3 | 1/6/2010 |
3 | 1/7/2010 |
Grouping on dates I could get then get the sum of quantity on that day however the final result set can't be aggregate due to user provided filters that may exclude some of these records down the road.
EDIT
To clarify, this is just a sample. The filters are irrelevant as I can join to the side to pull in details related to the record ID in the results.
The real data contains N records which increases weekly, the dates are never the same. There could be 2000 records with different start and end dates... That is what I want to generate a view for. I can right join onto the data to do the rest of what I need
I should also mention this is for past, present and future data. I would love to get rid of a temporary table of dates. I was using a recursive query to get all dates that exist within a 50 year span but this exceeds MAXRECURSION limits for a view, that I cannot use.
Answer
select RecordId,d.[Date], Qty/ COUNT(*) OVER (PARTITION BY RecordId) AS Qty
from EX join Dates d on d.Date between [Start Date] and [End Date]
ORDER BY RecordId,[Date]
NB: The below demo CTEs use the date datatype which is SQL Server 2008 the general approach should work for SQL2005 as well though.
Test Case
/*CTEs for testing purposes only*/
WITH EX AS
(
SELECT 1 AS RecordId,
cast('1/1/2010' as date) as [Start Date],
cast('1/5/2010' as date) as [End Date],
30000 AS Qty
union all
SELECT 2 AS RecordId,
cast('1/3/2010' as date) as [Start Date],
cast('1/9/2010' as date) as [End Date],
20000 AS Qty
),Dates AS /*Dates Table now adjusted to do greater range*/
(
SELECT DATEADD(day,s1.number + 2048*s2.number,'1990-01-01') AS [Date]
FROM master.dbo.spt_values s1 CROSS JOIN master.dbo.spt_values s2
where s1.type='P' AND s2.type='P' and s2.number <= 8
order by [Date]
)
select RecordId,d.[Date], Qty/ COUNT(*) OVER (PARTITION BY RecordId) AS Qty
from EX join Dates d on d.Date between [Start Date] and [End Date]
ORDER BY RecordId,[Date]
Results
RecordId Date Qty
----------- ---------- -----------
1 2010-01-01 6000
1 2010-01-02 6000
1 2010-01-03 6000
1 2010-01-04 6000
1 2010-01-05 6000
2 2010-01-03 2857
2 2010-01-04 2857
2 2010-01-05 2857
2 2010-01-06 2857
2 2010-01-07 2857
2 2010-01-08 2857
2 2010-01-09 2857
I think you can try this.
SELECT [Quantities].[RecordID], [Dates].[Date], SUM([Quantity])
FROM [Dates]
JOIN [Quantities] on [Dates].[Date] between [Quantities].[Start Date] and [End Date]
GROUP BY [Quantities].[RecordID], [Dates].[Date]
ORDER BY [Quantities].[RecordID], [Dates].[Date]

Resources