SQL Editing Year - sql

The query works but only give years 1985 values. How do I add unlimited amount of years (1985-2014)
use baseball;
SELECT CAST(tf.franchname AS CHAR(20)), s.yearID, s.lgid, AVG(s.salary)
FROM salaries s, teams t, teamsfranchises tf
WHERE s.teamID = t.teamID AND
t.franchID = tf.franchID AND
s.yearID = 1985 AND
(s.lgid='AL' OR s.lgid='NL') GROUP BY tf.franchname, s.yearID, s.lgid order BY
s.yearID;

You could just use BETWEEN.
Your where clause should then look like
(s.yearID BETWEEN 1985 AND 2014) and
Alternatively you could use the < and > operators:
(s.yearID >= 1984 and <= 2014)
If, for any reason you don't have a continous range of years (You only want 5 years). IN could also be an option:
s.yearID IN (1984, 1991, 1996, 2001, 2006)

Your query has a condition filtering on the year and s.yearID = 1985, you may want to change it using the keyword BETWEEN or removing it altogether depending of your need.

select cast(tf.franchname as char(20)), s.yearID, s.lgid, avg(s.salary)
from salaries s, teams t, teamsfranchises tf
where s.teamID = t.teamID and
t.franchID = tf.franchID and
(s.yearID between 1985 and 2014 )and
(s.lgid='AL' OR s.lgid='NL')
group by tf.franchname, s.yearID, s.lgid
order by s.yearID;

This is another view, when there is no data and still you want to get the year with zero count. Just check this link
In this you can create a temporary table which return the list of your years ie 1985 to 2015 , then just join with left outer join and see the magic.
I just get yourquery, you can replace with the accepted answer query too.
Declare #Startyear int = 1985
--1st approach to get continues year
;with yearlist as
(
select 1985 as year
union all
select yl.year + 1 as year
from yearlist yl
where yl.year + 1 <= YEAR(GetDate())
)
select year from yearlist order by year desc;
--2nd approach to get continues year
;WITH n(n) AS
(
SELECT 0
UNION ALL
SELECT n+1 FROM n WHERE n < (year(getdate()) -#Startyear)
)
SELECT year(DATEADD( YY, -n, GetDate()))
FROM n ORDER BY n
--take anyone approach and then join with your query
;with yearlist as
(
select 1985 as year
union all
select yl.year + 1 as year
from yearlist yl
where yl.year + 1 <= YEAR(GetDate())
)
select year from yearlist
left join
(
SELECT CAST(tf.franchname AS CHAR(20)), s.yearID, s.lgid, AVG(s.salary)
FROM salaries s, teams t, teamsfranchises tf
WHERE s.teamID = t.teamID AND
t.franchID = tf.franchID AND
s.yearID = 1985 AND
(s.lgid='AL' OR s.lgid='NL') GROUP BY tf.franchname, s.yearID, s.lgid order BY
s.yearID
) yourtable on yourtable.yearID = yearlist.year
order by year desc;

Related

Group by join date + add month in where sql

I have the following table:
PERSON
ID
Name
date_created
date_left
What I want is a list of all months and the amount of users joined and the amount of users that left.
I already have the following query: it returns the amount of new users that joined in the month that I pass:
select MONTH(date_created) 'Month', YEAR(date_created) 'Year', count(*) as 'New Users'
from person p
where YEAR(date_created) = 2018 and MONTH( p.date_created) = 5
group by MONTH(date_created), YEAR(date_created)
It returns what I want:
How would I edit this to include a year report and add the column 'Users left' next to the 'new users' one?
My result would be:
MONTH YEAR NEW USERS USERS LEFT
1 2019 10 5
I would "unpivot" the data using cross apply:
select v.[year], v.[month], sum(v.iscreated) as num_created,
sum(v.isleft) as num_left
from person p cross apply
(values (year(p.date_created), month(p.date_created), 1, 0),
(year(p.date_left), month(p.date_left), 0, 1)
) v([year], [month], iscreated, isleft)
group by v.[year], v.[month]
order by v.[year], v.[month];
The straight-forward approach is probably to full outer join all entries and all leaves. SQL Server makes this a bit awkward by not featuring USING, so we must use ON and COALESCE on month and year instead.
select
coalesce(pin.year, pout.year) as year,
coalesce(pin.month, pout.month) as month,
coalesce(pin.cnt, 0) as count_in,
coalesce(pout.cnt, 0) as count_out
from
(
select year(date_created) as year, month(date_created) as month, count(*) as cnt
from person
group by year(date_created), month(date_created)
) pin
full outer join
(
select year(date_left) as year, month(date_left) as month, count(*) as cnt
from person
group by month(date_left), year(date_left)
) pout on pout.year = pin.year and pout.month = pin.month
order by year, month;
Maybe you could do it with a SubSelect? Tried it right now with ORACLE Syntax, I'm not sure if it works in SQL-Server.
SELECT * FROM
(
select MONTH(date_created) 'Month_C', YEAR(date_created) 'Year_C', count(*) as 'New Users'
from person p
where YEAR(date_created) = 2018 and MONTH( p.date_created) = 5
group by MONTH(date_created), YEAR(date_created)
)created_user,
(
select MONTH(date_left) 'Month_L', YEAR(date_left) 'Year_L', count(*) as 'New Users'
from person p
where YEAR(date_left) = 2018 and MONTH( p.date_left) = 5
group by MONTH(date_left), YEAR(date_left)
) left_user
where created_user.Year_C = left_user.Year_L
and created_user.Month_C = left_user.Month_L

Sql server pivot table or inner join for daterange and sum column

I have a sql problem and not sure how to go about it because I'm not expert in SQL.
My outcome is:
**MONTH | 2011 | 2012**
1 8894108.372544 9200915.88732
2 8987154.877656 8188366.52079
3 8135004.323592 9383008.68889201
4 8374239.052416 9660272.13007201
5 9525066.469164 8578163.904876
6 7475397.368328 8606525.720634
7 8992114.52414401 9365367.617496
8 9358753.61943 11048712.924366
9 8853398.95447799 8499947.810022
10 8577323.223498 9225470.14965
11 9265685.67622799 9226157.80527
12 8887908.50775001 8318114.32842
Here is my sql.
SELECT MONTH( ts.TransactionDate) as [TransactionDate],
ISNULL(SUM(CAST([SalePrice] AS FLOAT)), 0) AS "SUM([SalePrice]) 2011", s.[SUM([SalePrice]]) 2012]
FROM [TABLESALES] ts
INNER JOIN (SELECT MONTH( t.TransactionDate) as [TransactionDate],
ISNULL(SUM(CAST([SalePrice] AS FLOAT)), 0) AS "SUM([SalePrice]) 2012"
FROM [TABLESALES] t
WHERE [TransactionDate] BETWEEN '2012-01-01' AND '2012-12-31' GROUP BY MONTH( t.TransactionDate)) s on s.TransactionDate = MONTH( ts.TransactionDate)
WHERE ts.TransactionDate BETWEEN '2011-01-01' AND '2011-12-31'
GROUP BY MONTH(ts.TransactionDate), s.[SUM([SalePrice]]) 2012]
ORDER BY [TransactionDate]
The Problem I have is the date range could be "2011" to "2015".
So I dont want to create inner joins multiple times to do this. Is there a better way and please can I have some code examples. Thanks in advance.
I know I am doing a single join when there is date range of 1 year. "2011 to 2012". But its not great when I have to do 2011-2015 so thats 4 years which means 4 inner joins?
This query will pivot the data:
; With Sales(Y, M, sale) as(
Select Year(TransactionDate), Month(TransactionDate), SalePrice From TABLESALES
Where TransactionDate >= '20120101' and TransactionDate< '20150101'
)
Select M, [2012], [2013], [2014]
From Sales
Pivot(
Sum(Sale)
For Y In ([2012], [2013], [2014])
) as Piv
You only need to update the where clause (start and end years) and add all years in the select and for

Normalization of Year bringing nulls back

I have the following query:
SELECT DISTINCT
YEAR(DateRegistered) as Years,
Months.[MonthName],
COUNT(UserID)as totalReg
FROM
Months WITH(NOLOCK)
LEFT OUTER JOIN
UserProfile WITH(NOLOCK)
ON
Months.MonthID = MONTH(DateRegistered)
AND
DateRegistered > DATEADD(MONTH, -12,GETDATE())
GROUP BY YEAR(DateRegistered), Months.[MonthName]
ORDER BY Months.[MonthName]
As you can tell this will always bring back 12 months worth of data. As such it is working, although there is a bug with this method.
It creates Null values in months where there is no data, now the record should exist(whole point of the query) but Year field is bringing Nulls which is something I dont want.
Now I understand the problem is because there is no data, how is it supposed to know what year?
So my question is - is there any way to sort this out and replace the nulls? I suspect I will have to completely change my methodology.
**YEAR** **MONTH** **TOTAL**
2013 April 1
2013 August 1
NULL December 0
2013 February 8
2013 January 1
2013 July 1
NULL June 0
2013 March 4
NULL May 0
NULL November 0
NULL October 0
2012 September 3
If you want 12 months of data, then construct a list of numbers from 1 to 12 and use these as offsets with getdate():
with nums as (
select 12 as level union all
select level - 1
from nums
where level > 1
)
select YEAR(thedate) as Years,
Months.[MonthName],
COUNT(UserID) as totalReg
FROM (select DATEADD(MONTH, - nums.level, GETDATE()) as thedate
from nums
) mon12 left outer join
Months WITH (NOLOCK)
on month(mon12.thedate) = months.monthid left outer join
UserProfile WITH (NOLOCK)
ON Months.MonthID = MONTH(DateRegistered) and
DateRegistered > DATEADD(MONTH, -12, GETDATE())
GROUP BY YEAR(thedate), Months.[MonthName]
ORDER BY Months.[MonthName];
I find something strange about the query though. You are defining the span from the current date. However, you seem to be splitting the months themselves on calendar boundaries. I also find the table months to be awkward. Why aren't you just using the datename() and month() functions?
Try this out:
;With dates as (
Select DateName(Month, getdate()) as [Month],
DatePart(Year, getdate()) as [Year],
1 as Iteration
Union All
Select DateName(Month,DATEADD(MONTH, -Iteration, getdate())),
DatePart(Year,DATEADD(MONTH, -Iteration, getdate())),
Iteration + 1
from dates
where Iteration < 12
)
SELECT DISTINCT
d.Year,
d.Month as [MonthName],
COUNT(up.UserID)as totalReg
FROM dates d
LEFT OUTER JOIN UserProfile up ON d.Month = DateName(DateRegistered)
And d.Year = DatePart(Year, DateRegistered)
GROUP BY d.Year, d.Month
ORDER BY d.Year, d.Month
Here's my attempt at a solution:
declare #UserProfile table
(
id bigint not null identity(1,1) primary key clustered
, name nvarchar(32) not null
, dateRegistered datetime not null default(getutcdate())
)
insert #UserProfile
select 'person 1', '2011-01-23'
union select 'person 2', '2013-01-01'
union select 'person 3', '2013-05-27'
declare #yearMin int, #yearMax int
select #yearMin = year(MIN(dateRegistered))
, #yearMax= year(MAX(dateRegistered))
from #UserProfile
;with monthCte as
(
select 1 monthNo, DATENAME(month, '1900-01-01') Name
union all
select monthNo + 1, DATENAME(month, dateadd(month,monthNo,'1900-01-01'))
from monthCte
where monthNo < 12
)
, yearCte as
(
select #yearMin yearNo
union all
select yearNo + 1
from yearCte
where yearNo < #yearMax
)
select y.yearNo, m.Name, COUNT(up.id) UsersRegisteredThisPeriod
from yearCte y
cross join monthCte m
left outer join #UserProfile up
on year(up.dateRegistered) = y.yearNo
and month(up.dateRegistered) = m.monthNo
group by y.yearNo, m.monthNo, m.Name
order by y.yearNo, m.monthNo
SQL Fiddle Version: http://sqlfiddle.com/#!6/d41d8/6640
You have to calculate the counts in a Derived Table (or a CTE) first and then join
untested:
SELECT
COALESCE(dt.Years, YEAR(DATEADD(MONTH, -Months.MonthID, GETDATE()))),
Months.[MonthName],
COALESCE(dt.totalReg, 0)
FROM
Months WITH(NOLOCK)
LEFT OUTER JOIN
(
SELECT
YEAR(DateRegistered) AS Years,
MONTH(DateRegistered) AS Mon,
COUNT(UserID)AS totalReg
FROM UserProfile WITH(NOLOCK)
WHERE DateRegistered > DATEADD(MONTH, -12,GETDATE())
GROUP BY
YEAR(DateRegistered),
MONTH(DateRegistered)
) AS dt
ON Months.MonthID = dt.mon
ORDER BY 1, Months.MonthID
I changed the order to Months.MonthID instead of MonthName and i added year because you might have august 2012 and 2013 in your result.

SQL Server query to find the number of years enrolled by member

I have 2 tables:
Table tbmembers
id name
----------
1 abc
2 def
3 ghi
Table tbmemberenrollment
id memberid(foreign key) startyear endyear
--------------------------------------------------
1 1 2007 2009
2 1 2011 2012
3 1 2013 2017
In the tbmemberenrollment table I want to calculate the number of years for which the member is enrolled till the current year, in this case result would be 6 years(2007, 2008, 2009, 2011, 2012, 2013)
I want to calculate the above result by SQL query, I don't know how to use for loops in SQL Server or how can we use cursor to get the above result, please help.....
Assuming your years might overlap (2007-2008 and then 2008-2009), the best option I see would be to create a Years lookup table and query against it, like such:
SELECT m.id, m.name, COUNT(DISTINCT y.yearfield) YearCount
FROM tblmembers m
CROSS JOIN YearLookup Y
INNER JOIN tbmemberenrollment me
ON m.id = me.memberid
AND YEAR(y.yearfield) >= YEAR(me.startyear)
AND YEAR(y.yearfield) <= YEAR(me.endyear)
AND YEAR(Y.yearField) <= YEAR(GetDate())
GROUP BY m.id, m.name
SQL Fiddle Demo
If your data won't have these overlapped years, then you can do something like this to get the results:
SELECT m.id, m.name,
SUM(
CASE
WHEN YEAR(me.endyear) > YEAR(getDate())
THEN YEAR(getDate())
ELSE YEAR(me.endyear)
END - YEAR(me.startyear) + 1
) totYears
FROM tblmembers m
LEFT JOIN tbmemberenrollment me on m.id = me.memberid
WHERE YEAR(me.startyear) <= YEAR(getDate())
GROUP BY m.id, m.name
SQL Fiddle Demo
EDIT: Using a recursive CTE vs. a Lookup table
While I would still recommend using the lookup table, sometimes that is not a viable option. In those cases, you can accomplish the same thing using a recursive CTE.
WITH years AS (
SELECT MAX(endyear) maxyear, MIN(startyear) minyear
FROM tbmemberenrollment
),
RecursiveCTE AS (
SELECT minyear yearfield
FROM years
UNION ALL
SELECT DATEADD(year, 1, yearfield)
FROM RecursiveCTE R
JOIN years T
ON R.yearfield < T.maxyear
)
SELECT m.id, m.name, COUNT(DISTINCT y.yearfield) YearCount
FROM tblmembers m
CROSS JOIN RecursiveCTE Y
INNER JOIN tbmemberenrollment me
ON m.id = me.memberid
AND YEAR(y.yearfield) >= YEAR(me.startyear)
AND YEAR(y.yearfield) <= YEAR(me.endyear)
AND YEAR(Y.yearField) <= YEAR(GetDate())
GROUP BY m.id, m.name
If your years do not overlap, you can do an aggregation:
select tm.name,
sum(case when endyear <= year(getdate()) then endyear - startyear + 1
else year(getdate()) - startyear + 1
end) as EnrollmentYears
from tmembers tm join
tbmemberenrollment tme
on tm.id = tme.memberid
where startyear <= year(getdate())
How about?:
SELECT Count(DISTINCT c.yr)
FROM (SELECT a.startyear AS yr
FROM tbmemberenrollment a
UNION
SELECT b.endyear AS yr
FROM tbmemberenrollment b) AS c
This is a good condidate for CTE query.
;with memberEnrollment_CTE(memberid, Eyear, Startyear, Endyear)
as
(
SELECT memberid, startyear as EYear, startyear, endyear
from dbo.tbmemberenrollment
union all
select memberid, Eyear+1, Startyear, Endyear
from memberEnrollment_CTE
where endyear>yyear
)
select distinct memberid, Eyear as EnrolledYear from memberEnrollment_CTE where
Eyear<= YEAR(GETDATE())
This even works for overlap enrolled years. And should give you all the years, so you can run count() queries over this or join with member table.

Sqlite query comparison multiple times

I have the following schemas (sqlite):
JournalArticle(articleID, title, journal, volume, year, month)
ConferenceArticle(articleID, title, conference, year, location)
Person(name, affiliation)
Author(name, articleID)
I'm trying to get the names of all authors who have number of conferences articles >= journal articles in every year from 2000-2018 inclusive. If an author has 0 articles in each category in a year then the condition still holds. The only years that matter are 2000-2018
The query would be much easier if it was over all years since I could count the journal articles and conferences articles and make a comparison then get the names. However, I'm stuck when trying to check over every year 2000-2018.
I of course don't want to do repetitive queries over all the years. I feel like I may need to group by year but I'm not sure. So far I've been able to get all articles of both types from 2000-2018 as one large table but I'm not sure what to do next.:
select articleID, year
from JournalArticle
where year >= 2000 and year <= 2018
union
select articleID, year
from ConferenceArticle
where year >= 2000 and year <= 2018
Hmmm. Let's start by getting a count for each author and year:
select a.name, year, sum(is_journal), sum(is_conference)
from ((select ja.article_id, ja.year, 1 as is_journal, 0 as is_conference
from journalarticle ja
) union all
(select ca.article_id, ca.year, 0 as is_journal, 1 as is_conference
from conferencearticle ca
)
) jc join
authors a
on a.article_id = jc.article_id
group by a.name, jc.year
Now, you can aggregate again to match the years that match the conditions:
select ay.name
from (select a.name, year, sum(is_journal) as num_journal, sum(is_conference) as num_conference
from ((select ja.article_id, ja.year, 1 as is_journal, 0 as is_conference
from journalarticle ja
) union all
(select ca.article_id, ca.year, 0 as is_journal, 1 as is_conference
from conferencearticle ca
)
) jc join
authors a
on a.article_id = jc.article_id
group by a.name, jc.year
) ay
where (jc.year >= 2000 and jc.year <= 2018) and
num_journal >= num_conference
group by ay.name;
Sounds like you could use a COALESCE in the GROUP BY
SELECT a.name,
COALESCE(j.year, c.year) as "year",
COUNT(j.articleID) AS JournalArticles,
COUNT(c.articleID) AS ConferenceArticles
FROM Author a
LEFT JOIN JournalArticle j ON (j.articleID = a.articleID AND j.year BETWEEN 2000 AND 2018)
LEFT JOIN ConferenceArticle c ON (c.articleID = a.articleID AND c.year BETWEEN 2000 AND 2018)
WHERE (j.year IS NOT NULL OR c.year IS NOT NULL)
GROUP BY a.name, COALESCE(j.year, c.year)
HAVING COUNT(c.articleID) >= COUNT(j.articleID)