getting mutlitple row for a Oracle SQL query - sql

I have a table as
userid cycleid ratings
1 13 5
1 14 6
1 15 7
I have to display data as
userid 2011 2012 2013
1 5 6 7
as you can see cycleid 13 is for year 2011, cycleid 14 for year 2012 and cycleid 15 for year 2013
MY QUERY
SELECT PER.USERID,
(SELECT max(PER1.RATING) FROM PERFRATINGS PER1 WHERE PER1.CYCLEID = 13) as 2011,
(SELECT max(PER2.RATING) FROM PERFRATINGS PER2 WHERE PER2.CYCLEID = 14) as 2012,
(SELECT max(PER3.RATING) FROM PERFRATINGS PER3 WHERE PER3.CYCLEID = 15) as 2013
FROM PERFRATINGS PER
Where PER.USERID = 1
gives multiple row (3 times)
userid 2011 2012 2013
1 5 6 7
1 5 6 7
1 5 6 7
I want everything in single row.

I suggest to use pivot
select * from
(select userid, cycleid, ratings from perfratings)
pivot
(max(ratings) for (cycleid) in (
12 as 2012,
13 as 2013,
14 as 2014
))

You could fix your query using group by or distinct. A more efficient version would use pivot (if available in your version of Oracle) or conditional aggregation:
SELECT PER.USERID,
max(case when PER1.CYCLEID = 13 then PER1.RATING end) as 2011,
max(case when PER1.CYCLEID = 14 then PER1.RATING end) as 2012,
max(case when PER1.CYCLEID = 15 then PER1.RATING end) as 2013
FROM PERFRATINGS PER
Where PER.USERID = 1
GROUP BY PER.USERID;
In a sense, the group by is redundant when you are looking only at one user. However, you can remove the where and see the results for all users.

Related

How to use 2 columns as "key" to get MAX value of selection (and on to next "key") in a SQL query

using a SQL query I am trying to get a max value from multiple rows, using 2 columns as 'key', and then sum them and move on t next 'key'
Here is an example table. It has years, userid and points. Each year has several weeks.
What I want to do is to take each users MAX points for each year and SUM them.
year
userid
week
points
2020
1
1
3
2020
1
3
3
2020
1
3
5
2020
1
4
12
2020
2
1
4
2020
2
2
4
2020
2
3
6
2020
2
4
10
2021
1
1
4
2021
1
2
5
2021
1
3
8
2021
1
4
9
2021
2
1
3
2021
2
2
6
2021
2
3
7
2021
2
4
13
I'd like the result for each year to be
User 1:
2020, 1, 12
2021, 1, 9
User 2:
2020, 2, 10
2021, 2, 13
...and after summing them, sorted by points:
userid
points
2
33
1
21
...and so forth (adding on users and years)
Any help is very much appreciated.
Per Gordon's helpful answer this is the query:
SELECT username, userdb.userid, SUM(points) as points FROM (SELECT standing.*, row_number() over (partition by standing.userid, year ORDER BY points desc) AS seqnum FROM standing) t JOIN userdb on userdb.userid = t.userid WHERE seqnum = 1 GROUP BY userid ORDER BY points DESC
You can use two levels of aggregation:
select userid, sum(max_points)
from (select userid, year, max(points) as max_points
from t
group by userid, year
) uy
group by userid;
Alternatively, you could handle this by filtering such as by using a window function:
select userid, sum(points)
from (select t.*,
row_number() over (partition by userid, year order by points desc) as seqnum
from t
) t
where seqnum = 1
group by userid;

more than one AVG column with diffrent conditions

I have a table as follows:
id year value
1 2012 10
2 2013 7
3 2013 7
4 2014 8
5 2014 10
6 2015 6
7 2011 12
I need to write a query which gives the AVG value of the last 4 years from today. Meaning that if today is 2016 then the AVG is on 2015,2014,2013.
Basicly this could be done with 3 queries:
Select avg(value) as a
from tab
where year=2015
and
Select avg(value) as b
from tab
where year=2014
and
Select avg(value) as c
from tab
where year=2013
The results based on the given values should be:
2013 7
2014 9
2015 6
Since all of them is on the same table... How can I do that in one query (postgresql)?
it should be without a WHERE.
Something like:
Select avg(with condition) as a, avg(with condition) as b, avg(with condition) as c
from tab
You can group by year and constrict to the years you want in your where clause
select avg(value), year
from tab
where year in (2013,2014,2015)
group by year
The query above will give you 3 separate rows. If you prefer a single row then you can use conditional aggregation instead of a group by
select
avg(case when year = 2013 then value end) as avg_2013,
avg(case when year = 2014 then value end) as avg_2014,
avg(case when year = 2015 then value end) as avg_2015,
from tab
where year in (2013,2014,2015)
select
avg(case when year = date_part('year', NOW()) then value end) as avg_2016,
avg(case when year = ((date_part('year', NOW())) - 1 ) then value end) as avg_2015,
avg(case when year = ((date_part('year', NOW())) - 2 ) then value end) as avg_2014,
avg(case when year = ((date_part('year', NOW())) - 3 ) then value end) as avg_2013
from tab

Classifying months in periods

Suppose I have 2 years of data. From January 2010 to Dec 2011.
I want to classify each of the months as periods. So January 2010 will be my 1, February 2010 my 2, and so on until December 2011 my 24 period.
I know I could do it something like:
select
year,mn,
case when year=2010 and mn=01 then 1
else when year=2010 and mn=02 then 2
else when year=2010 and mn=03 then 3
//and so on until // else when year=2011 and mn=12 then 24 end
from mytable;
The result would be something like:
year mn period
2010 1 1
2010 2 2
2010 3 3
2010 4 4
2010 5 5
2010 6 6
2010 7 7
2010 8 8
2010 9 9
2010 10 10
2010 11 11
2010 12 12
2011 1 13
2011 2 14
2011 3 15
2011 4 16
2011 5 17
2011 6 18
2011 7 19
2011 8 20
2011 9 21
2011 10 22
2011 11 23
2011 12 24
I want to avoid this kind of long and not wise method.
select
year, mn,
row_number() over (order by year, mn) as period
from t
No need for fancy windowing functions. Just do it the simple way. For a given {epoch-year} and {epoch-month} (e.g., 2010 and 1 respectively), the formula
( ( 12*year + mn ) - ( 12*{epoch-year} + {epoch-month} )
will give you the offset in month from the epoch. Add 1 to that and you have your period number. That leads you to something like this:
select year ,
mn ,
( ( 12*year + mn )
- ( 12*{epoch-year} + {epoch-month} )
) + 1 as period
...
from some-table
where year > {epoch-year}
OR ( year = {epoch-year} and mn >= {epoch-month} )
IF you don't have a specific epoch in mind, you can do something like this:
select t.year ,
t.mn ,
( ( 12*year + mn )
- ( 12*epoch.year + epoch.month )
) + 1 as period
...
from ( select year,mn
from some-table
order by year , mn
limit 1
) epoch
cross join some-table t
You should note that one can come up with a formula to number periods based on period lengths longer than 1 month: just compute the offset in months and and use integer division to divide that offset by the period length in months, thus getting to the sequential period number, something like
( ( 12*year + mn )
- ( 12*2010 + 1 )
) DIV 3 + 1 as period
should give you periods of 3 months in length
A cheap version for this particular case:
SELECT year, mn, (year - 2010) * 12 + mn AS period
FROM tbl;
This would also account for months that may be missing in your data.
And it would give you consistent numbers even when only selecting some rows.

SQL Server - Get a count even if there are no results corresponding table?

The query below returns problems created for a developer in last 12 months. The query below works fine, but I wanted this to return 0 when there is no match.
SELECT
count(1) as count,
MONTH(pro.[DATE_CREATED]) AS MONTHNAME,
YEAR(pro.[DATE_CREATED]) AS YEARNAME ,
problemStatus.TypeName
FROM
[TestDB].[dbo].PROBLEMS AS pro
LEFT JOIN
[TestDB].[dbo].PROBLEMSTATUS AS problemStatus ON problemStatus.ID = pro.ID_STATUS
LEFT JOIN
[TestDB].[dbo].CUSTOMER AS cust ON pro.ID = cust.ID
WHERE
pro.ID = 1010101010
AND pro.[DATE_CREATED] >= DATEADD(m, -6, current_timestamp)
GROUP BY
MONTH(pro.[DATE_CREATED]),
YEAR(pro.[DATE_CREATED]),
problemStatus.type
ORDER BY
MONTH(pro.[DATE_CREATED]) DESC
Right now the query returns,
Count Month Year Status
1 12 2013 Fixed
1 11 2013 Fixed
1 9 2013 Fixed
1 8 2013 Fixed
1 2 2014 Fixed
1 1 2014 Opened
1 1 2014 Fixed
I want this to return
Count Month Year Status
1 12 2013 Fixed
0 12 2013 Opened
1 11 2013 Fixed
0 11 2013 Opened
1 9 2013 Fixed
0 9 2013 Opened
1 8 2013 Fixed
0 8 2013 Opened
1 2 2014 Fixed
0 2 2014 Opened
1 1 2014 Opened
1 1 2014 Fixed
If you have data in the table for all months, but just not for that ID, then try this:
SELECT sum(case when pro.ID = 1010101010 then 1 else 0 end) as count,
MONTH(pro.[DATE_CREATED]) AS MONTHNAME,
YEAR(pro.[DATE_CREATED]) AS YEARNAME ,
coalesce(max(case when pro.ID = 1010101010 then problemStatus.TypeName end), 'Opened') as problemStatus
FROM [TestDB].[dbo].PROBLEMS AS pro
left JOIN [TestDB].[dbo].PROBLEMSTATUS AS problemStatus
ON problemStatus.ID = pro.ID_STATUS
left JOIN [TestDB].[dbo].CUSTOMER AS cust
ON pro.ID = cust.ID
WHERE pro.[DATE_CREATED] >= DATEADD(m, -6, current_timestamp)
GROUP BY MONTH(pro.[DATE_CREATED]), YEAR(pro.[DATE_CREATED]) , problemStatus.type
ORDER BY MONTH(pro.[DATE_CREATED]) DESC;

How to get month and year in single column and grouping the data for all the years and months?

For the below query (sdate is column name and table name is storedata)
Collapse
WITH TotalMonths AS (SELECT T1.[Month], T2.[Year]
FROM ((SELECT DISTINCT Number AS [Month]
FROM MASTER.dbo.spt_values WHERE [Type] = 'p' AND Number BETWEEN 1 AND 12) T1 CROSS JOIN
(SELECT DISTINCT DATEPART(year, sdate) AS [Year]
FROM storedata) T2))
SELECT CTE.[Year], CTE.[Month], ISNULL(T3.[Sum], 0) areasum
FROM TotalMonths CTE LEFT OUTER JOIN (
SELECT SUM(areasft) [Sum], DATEPART(YEAR, sdate) [Year], DATEPART(MONTH, sdate) [Month]
FROM storedata
GROUP BY DATEPART(YEAR, sdate) ,DATEPART(MONTH, sdate)) T3
ON CTE.[Year] = T3.[Year] AND CTE.[Month] = T3.[Month] WHERE CTE.[Year]>'2007'
ORDER BY CTE.[Year], CTE.[Month]
I am getting result set like below.
YEAR MONTH AREASUM
2008 1 0
2008 2 1193
2008 3 4230
2008 4 350
2008 5 2200
2008 6 4660
2008 7 0
2008 8 6685
2008 9 0
2008 10 3051
2008 11 7795
2008 12 2940
2009 1 1650
2009 2 3235
2009 3 2850
2009 4 6894
2009 5 3800
2009 6 2250
2009 7 1000
2009 8 1800
2009 9 1550
2009 10 2350
2009 11 0
2009 12 1800
But I have to combine both month and year in single column. The reult set should like below.
JAN/08 O
FEB/08 1193
.. ..
.. ..
DEC/O9 1800
How can I modify my query? (I should display for all the years and months even if there is no area for a month)
Regards,
N.SRIRAM
Try:
SELECT CONVERT(VARCHAR(3), DATENAME(MONTH, CTE.Month), 7) + '/' + RIGHT(CTE.Year, 2)
instead of using your first 2 columns from your SELECT.
You seem to be saying that you're getting the right data from your original query, but the wrong format. So
Make a view out of the query you originally posted.
Build a SELECT query based on that view to give you the format you want.
Let's say you do this:
CREATE VIEW wibble AS <your original query goes here>
Then you can just query wibble to correct the formatting.
select
case
when month = 1 then 'Jan/'
when month = 2 then 'Feb/'
when month = 3 then 'Mar/'
when month = 4 then 'Apr/'
when month = 5 then 'May/'
when month = 6 then 'Jun/'
when month = 7 then 'Jul/'
when month = 8 then 'Aug/'
when month = 9 then 'Sep/'
when month = 10 then 'Oct/'
when month = 11 then 'Nov/'
when month = 12 then 'Dec/'
else 'Err'
end || substring(cast(year as CHAR(4)), 3, 2) as yearmonth,
areasum from wibble;