How to replace all values in grouped column except first row - sql

I have table like this:
ID Region CreatedDate Value
--------------------------------
1 USA 2016-01-01 5
2 USA 2016-02-02 10
3 Canada 2016-02-02 2
4 USA 2016-02-03 7
5 Canada 2016-03-03 3
6 Canada 2016-03-04 10
7 USA 2016-03-04 1
8 Cuba 2016-01-01 4
I need to sum column Value grouped by Region and CreatedDate by year and month. The result will be
Region Year Month SumOfValue
--------------------------------
USA 2016 1 5
USA 2016 2 17
USA 2016 3 1
Canada 2016 2 2
Canada 2016 3 13
Cuba 2016 1 4
BUT I want to replace all repeated values in column Region with empty string except first met row. The finish result must be:
Region Year Month SumOfValue
--------------------------------
USA 2016 1 5
2016 2 17
2016 3 1
Canada 2016 2 2
2016 3 13
Cuba 2016 1 4
Thank you for a solution. It will be advantage if solution will replace also in column Year

You need to use SUM and GROUP BY to get the SumOfValue. For the formatting, you can use ROW_NUMBER:
WITH Cte AS(
SELECT
Region,
[Year] = YEAR(CreatedDate),
[Month] = MONTH(CreatedDate),
SumOfValue = SUM(Value),
Rn = ROW_NUMBER() OVER(PARTITION BY Region ORDER BY YEAR(CreatedDate), MONTH(CreatedDate))
FROM #tbl
GROUP BY
Region, YEAR(CreatedDate), MONTH(CreatedDate)
)
SELECT
Region = CASE WHEN Rn = 1 THEN c.Region ELSE '' END,
[Year],
[Month],
SumOfValue
FROM Cte c
ORDER BY
c.Region, Rn
ONLINE DEMO
Although this can be done in TSQL, I suggest you do the formatting on the application side.
Query that follows the same order as the OP.

Related

Calculate value using previous and current month

I have below three tables
Stock Table
ID GlobalStock Date Country
1 10 2017/01/01 India
1 20 2017/01/01 India
2 5 2017/02/01 Africa
3 6 2017/08/01 Japan
4 7 2017/04/01 Japan
5 89 2017/08/01 Japan
2 10 2017/03/01 Japan
5 8 2017/03/01 Japan
1 20 2017/02/01 India
ShipFile
ID GlobalStock Date Country
2 10 2017/03/01 Africa
3 60 2017/08/01 India
11 70 2017/08/01 India
1 8 2017/02/01 India
1 9 2017/02/01 India
2 4 2017/03/01 Japan
2 5 2017/04/01 Japan
5 3 2017/03/01 Japan
3 8 2017/08/01 Japan
SalesFiles
ID GlobalStock Date Country
2 10 2017/03/01 India
2 20 2017/03/01 Africa
3 30 2017/08/01 Japan
7 5 2017/02/01 Japan
8 8 2018/01/01 Japan
1 9 2017/02/01 India
1 70 2017/02/01 Africa
13 10 2017/08/01 Japan
10 60 2017/11/01 Japan
I want to calculate -> StockTable(Month - 1) + ShipFile (Month) - Sales (Month)
For example
For ID 1 suppose we are considering Jan (GlobalStock -> 10 + 20) data then in other tables we must take Feb values and country should be same for all tables.
So calculation would be
(10 + 20) + (8 + 9) - (9) = 38
If we consider Feb ID of stocktable then we must consider March data from other tables and so on..
the joining all table i am considering ID and Country.
You can query using subquery or cte as below:
;With cte_Stock as (
Select ID, [Date], Country, sum(GlobalStock) Sum_GlobalStock from Stock
group by Id, [Date], Country
), cte_ShipFiles as (
Select ID, [Date], Country, sum(GlobalStock) Sum_GlobalStock from ShipFile
group by Id, [Date], Country
)
, cte_SalesFiles as (
Select ID, [Date], Country, sum(GlobalStock) Sum_GlobalStock from SalesFiles
group by Id, [Date], Country
)
select s.ID, s.[Date], sf.[Date], s.Country,
YourOutput = s.Sum_GlobalStock+sf.Sum_GlobalStock-sales.Sum_GlobalStock
from cte_Stock s
join cte_ShipFiles sf
on s.ID = sf.ID
and s.Country = sf.Country
and s.[Date] = dateadd(mm,-1, sf.[Date])
join cte_SalesFiles sales
on s.ID = sales.ID
and s.Country = sales.Country
and s.[Date] = dateadd(mm,-1, sales.[Date])
Output as below:
+----+------------+------------+---------+------------+
| ID | Date | Date | Country | YourOutput |
+----+------------+------------+---------+------------+
| 1 | 2017-01-01 | 2017-02-01 | India | 38 |
| 2 | 2017-02-01 | 2017-03-01 | Africa | -5 |
+----+------------+------------+---------+------------+
Here is an approach with derived tables:
DECLARE #CurrentMonth date = '20180101'
DECLARE #NextMonth date = DATEADD(MONTH,1,#CurrentMonth)
SELECT s.Country, SUM(s.GlobalStock) + ShipSum - SaleSum
FROM stock s
LEFT JOIN (SELECT ISNULL(SUM(GlobalStock),0) ShipSum, Country
FROM ShipFile
WHERE Date >= #NextMonth
AND Date <= EOMONTH(#NextMonth)
GROUP BY Country) sh on s.Country = sh.Country
LEFT JOIN (SELECT ISNULL(SUM(GlobalStock),0) SaleSum, Country
FROM SalesFile
WHERE Date >= #NextMonth
AND Date <= EOMONTH(#NextMonth)
GROUP BY Country) sa on s.Country = sa.Country
WHERE s.Date >= #CurrentMonth
AND s.Date <= EOMONTH(#CurrentMonth)
GROUP BY s.Country, ShipSum, SaleSum
Notes:
This uses Country for the joins because ID seems to change between tables.
It also uses a date range assuming that the day portion of your date column is not always the first of the month - if it is always the first that can be simplified to date = #CurrentMonth or date = #NextMonth

Access SQL count number of people group by week number

I need to count how many people are working given a week number.
Here's my people table (date is US format) :
name | surname| date_of_entry | date_of_exit
-----|--------|---------------|-------------
foo | bar | 1/1/2006 | 1/8/2006
foo1 | bar1 | 1/5/2010 |
foo2 | bar2 | 2/3/2015 | 3/4/2015
and I'd like for a given year to have all weeks number with the proper number of people working during this period.I hope you understand me because english is not my native language sorry.
I've done some research and from what i understand i need to create a table with all weeks starting from 1/1/2006 and ending "now" (because people with no exit date are still working) according to the example above to be able to test for each person if he was working during this week so I can count him in my query.
I'm still a student programmer but it seems to be a pretty complex SQL query to me.
Expected output for a query for year 2006 (with the new year starting a monday) :
week_number | count
------------|------
1 | 1
2 | 1
3 | 0
etc.. | 0
until 1/5/2010 where all weeks have 1 in the field "count"
and then query for year 2015 :
week_number | count
------------|------
1 | 1
2 | 1
... | 1
9 | 2
... | 2
14 | 1
... | 1
If anybody can help me to resolve this it would be awesome, thanks!
You are lucky I have to change my mind for 15 minutes.
Step 1 : Create Calendar table
Create a new table named Calendar with the following fields
id : autonumber
Cal_Year : number
Cal_Week : number
In a module, add the following code and execute it (F5) :
Private Sub Create_Calendar_table()
Dim Y As Integer
Dim W As Integer
For Y = 2006 To 2016
For W = 1 To 52
DoCmd.RunSQL "INSERT INTO Calendar (cal_year, cal_week) VALUES (" & Y & "," & W & ")"
Next W
Next Y
End Sub
Calendar table is now ready to use :
ID Cal_year Cal_week
1 2006 1
2 2006 2
3 2006 3
4 2006 4
5 2006 5
and so on...
Step 2 : Create the query
Note that I am in Europe so my dates are DD/MM. This won't affect your results.
I decompose so you understand the process.
First we need to create a date from the year/week in the calendar table, this can be achieved like this
SELECT Cal_year, Cal_week, DateAdd("ww",Cal_week,DateSerial(Cal_year,1,1)) AS thedate
FROM Calendar
Cal_year Cal_week thedate
2006 1 8/01/2006
2006 2 15/01/2006
2006 3 22/01/2006
2006 4 29/01/2006
2006 5 5/02/2006
and so on...
Next, since we will work on ranges of dates, it is important a to attribute the current date when the people's exit_date is NULL, like this :
nz(date_of_exit, Now)
The field is prepared.
Then, the trick.
We need to JOIN our calendar table with the people table in manner that will return a record for every week on which a person is present.
The key to achieve this is the ON...BETWEEN...AND
SELECT C.Cal_year, C.Cal_Week, P.pname, P.psurname, P.date_of_entry, nz(P.date_of_exit, Now) AS exit_date
FROM [Calendar] C
INNER JOIN ( SELECT [Name] AS pname, [surname] as psurname, date_of_entry, date_of_exit
FROM people
) P
ON (DateAdd("ww",C.Cal_week,DateSerial(C.Cal_year,1,1)) BETWEEN P.date_of_entry AND nz(P.date_of_exit, Now))
ORDER BY C.Cal_year, C.Cal_Week
Cal_year Cal_Week pname psurname date_of_entry exit_date
2006 1 foo bar 1/01/2006 8/01/2006
2010 1 foo1 bar1 5/01/2010 22/04/2016 13:04:39
2010 2 foo1 bar1 5/01/2010 22/04/2016 13:04:39
2010 3 foo1 bar1 5/01/2010 22/04/2016 13:04:39
2010 4 foo1 bar1 5/01/2010 22/04/2016 13:04:39
Note that if you need ALL WEEKS since 2006, even those for which nobody is present, just change the INNER JOIN with a LEFT JOIN
And finally, we exploit the previous query to count the presences by doing a GROUP BY on year and week of the calendar table, and we specify the year 2015 in the WHERE clause otherwise it will count everything since 2006. Which implies that it is very easy to count the presences for any year.
SELECT yyyy, ww , count(*) AS cnt
FROM
(
SELECT C.Cal_year AS yyyy, C.Cal_Week AS ww
FROM [Calendar] C
INNER JOIN ( SELECT [Name] AS pname, [surname] as psurname,
date_of_entry,
date_of_exit
FROM people
) P ON (DateAdd("ww",C.Cal_week,DateSerial(C.Cal_year,1,1)) BETWEEN P.date_of_entry AND nz(P.date_of_exit, Now))
)
WHERE yyyy=2015
GROUP BY yyyy, ww
ORDER BY yyyy, ww
yyyy ww cnt
2015 1 1
2015 2 1
2015 3 1
2015 4 1
2015 5 1
2015 6 1
2015 7 1
2015 8 1
2015 9 2
2015 10 2
2015 11 2
2015 12 2
2015 13 2
2015 14 1
2015 15 1
2015 16 1
Well, it took me 40 minutes finally...
You can create a query to find the weeks working:
Select
[name],
surname,
year,
week
From
PeopleTable,
WeekTable
Where
(date_of_entry <= week_start And DateDiff("d", week_start, date_of_exit) >= 3)
Or
(date_of_entry >= week_start And date_of_exit <= week_end
And
DateDiff("d", date_of_entry, date_of_exit >= 3)
Or
(DateDiff("d", date_of_entry, week_end) >= 3 And date_of_exit >= week_end)
Group By
[name],
surname,
year,
week
Now, save this and create a new query where you use WeekTable as source (to list all weeks) with an outer join to the query above (to list the worked weeks). In this, Group By the year and week and add a count to get the count of working employees for each week.

SQL Server Count number of overlaps (date ranges)

I have a table which stores vehicles and the dates they are rented out. I would like to find out if the dates overlap and the count of overlaps for a vehicle in SQL Server 2008. The result I am expecting is as follows.
ID Vehicle StartDate EndDate Overlap
==============================================================
1 Ford Focus 01/01/2014 31/01/2014 1
2 Ford Focus 20/01/2014 20/02/2014 1
3 Ford Focus 01/03/2014 28/03/2014 0
4 Mercedes 18/03/2014 24/03/2014 0
5 Mercedes 01/07/2014 31/07/2014 2
6 Mercedes 15/07/2014 31/07/2014 2
7 Mercedes 25/07/2014 25/08/2014 2
You can try this query:
select *, (select count(*) from test
where not (v.StartDate > EndDate or v.EndDate < StartDate)
and Vehicle = v.Vehicle and ID != v.ID) as Overlap
from test v
Sql fiddle demo.

Need to print highest year and their highest quarter in SQL Server 2012

I have a requirement to print the corresponding highest year and highest quarter for a given column.
Input is in a table:
cityprogram year quarter
=========== ==== =======
Abc 1998 1
Abc 1999 4
Abc 1999 4
Abc 1998 3
xyz 1998 4
xyz 1998 1
xyz 2000 3
It should print
Abc 1999 4
xyz 2000 3
I tried many joins, max conditions, I seem to get quarter 4 and 4 for both of them :( thanks
Use a window function like ROW_NUMBER in a common-table-expression:
WITH CTE AS(
SELECT [cityprogram], [year], [quarter],
RN = ROW_NUMBER() OVER (
PARTITION BY [cityprogram]
ORDER BY [year] DESC, [quarter] DESC)
FROM dbo.TableName
)
SELECT [cityprogram], [year], [quarter]
FROM CTE
WHERE RN = 1
DEMO
CITYPROGRAM YEAR QUARTER
Abc 1999 4
xyz 2000 3
ROW_NUMBER returns only one row per group even if there are ties(cityprograms with the same highest year+quarter). If you then want to show all highest you can replace ROW_NUMBER with DENSE_RANK.

DB2 SQL SUM and GROUPING

I am having problems with querying and grouping.
I am needing the following output:
officr, cbal, sname
ABC, 500.00, TOM JONES
ABC, 200.00, SUE JONES
ABC TOTAL 700.00
RAR, 100.10, JOE SMITH
RAR, 200.05, MILES SMITH
RAR TOTAL 300.15
SQL below produces the error:
[DB2 for i5/OS]SQL0122 - Column SNAME or expression in SELECT list not valid.
SELECT
lnmast.officr, SUM(LNMAST.CBAL), lnmast.sname
FROM
LNMAST
WHERE LNMAST.RATCOD IN (6,7,8) AND STATUS NOT IN ('2','8')
group by lnmast.officr
GROUP BY GROUPING SETS is a POWERFUL tool for grouping/cubing data. It lets you combine non-aggregated data with aggregated data in one query result.
SELECT lnmast.officr, SUM(LNMAST.CBAL), lnmast.sname
FROM LNMAST
WHERE LNMAST.RATCOD IN (6,7,8)
AND STATUS NOT IN ('2','8')
GROUP BY GROUPING SETS ((lnmast.officr, lnmast.sname),(lnmast.officr))
An example from IBM DOCS: www.ibm.com/support/knowledgecenter/en/... :
SELECT WEEK(SALES_DATE) AS WEEK,
DAYOFWEEK(SALES_DATE) AS DAY_WEEK,
SALES_PERSON, SUM(SALES) AS UNITS_SOLD
FROM SALES
WHERE WEEK(SALES_DATE) = 13
GROUP BY GROUPING SETS ( (WEEK(SALES_DATE), SALES_PERSON),
(DAYOFWEEK(SALES_DATE), SALES_PERSON))
ORDER BY WEEK, DAY_WEEK, SALES_PERSON
This results in:
WEEK DAY_WEEK SALES_PERSON UNITS_SOLD
----------- ----------- --------------- -----------
13 - GOUNOT 32
13 - LEE 33
13 - LUCCHESSI 8
- 6 GOUNOT 11
- 6 LEE 12
- 6 LUCCHESSI 4
- 7 GOUNOT 21
- 7 LEE 21
- 7 LUCCHESSI 4