Average without grouping or subquery - sql

I realize this is an odd question, but I'd like to know if this is possible.
Let's say I have a DB with ages and IDs. I need to compare each ID's age to the average age, but I can't figure out how to do that without grouping or subqueries.
SELECT
ID,
AGE - AVG(AGE)
FROM
TABLE
I'm trying to get something like the above, but obviously that doesn't work because ID isn't grouped, but I group, then it calculates the average for each group, and not the table as a whole. How can I get a global average without a subquery?

SELECT ID,
AGE -
AVG(AGE) OVER (partition by ID) as age_2
FROM Table
I just read is global avg
SELECT ID,
AGE -
AVG(AGE) OVER () as age_2
FROM Table

The window logic for average age is:
SELECT ID, AGE - ( AVG(AGE) OVER () )
FROM TABLE;
You do not want ORDER BY in the partitioning clause.

How can I get a global average without a subquery?
Use a variable.
DECLARE #AvgAge decimal(4,3)
SELECT #AvgAge = AVG(AGE) FROM TABLE
SELECT ID, AGE - #AvgAge [AgeDiff] FROM TABLE

Related

SQL Server Count field values without merge

How do I create a COUNT column to count the repetitive values?
And I want to keep the table EXACTLY as below but add the last column (count_id).
The values at the left come from a JOIN so they are "equal".
Thanks! (I tried a lot)
You just want count(*) as a window function:
select t.*,
count(*) over (partition by id, name, department) as count_id
from t;

join and group by in SQL

I have two tables that I was going to join, but I understand it's more efficient to use CREATE VIEW. This is what I have:
CREATE OR REPLACE VIEW view0_joinedTablesGrouped
AS
Select table1.*,table2.*
FROM table1
inner join table2 on table1.col =
table2.matchingcol
group by table2.matchingcol;
which causes the following error:
ERROR: column "table1.col" must appear in the GROUP BY clause or be
used in an aggregate function
LINE 3: Select table.*,table2.*
Group By cannot do what you are trying to do.
Consider a simple table:
Name Age
-------
Ann 10
Bill 10
Chris 11
If you try to group by age with:
Select * from Table group by Age
What, exactly, do you expect to appear in the Name column for Age=10? Ann, or Bill or both or neither or ....? There is no good answer.
So, when you group by, every column in the output has to be an aggregate – that means a function of every row in the group.
So these are valid:
Select Age, Count(*) from Table group by Age
Select Age, Max( Length(Name)) from Table group by Age
Select Age, Max(Name) from Table group by Age
But this is impossible to do, and isn't valid:
Select Age,Name from Table group by Age
So your select * is the problem -- you can't just select column values because when you group by there's a whole group of column values for every output row, and you can't stuff all those values into one column of one row.
As for using a view, #systemjack's comment is correct.

Select and group by two columns

I have a table that stores the number of users who score in a particular range. I am trying to design a query that will show the total number of students who scored in that range. The table has repetitions since its a log table. here is there table
Here is my query
select id, min, max, count(Score)
from Scorelogs
I know I have to use "COUNT" in the query but I don't know how to group by the min and max.
You want to just group by the range:
select "min", "max", sum(score)
from data
group by "min", "max"
order by 1, 2;
Note that min and max are very poor names for columns, because they conflict with the built in functions that have the same names.
Use as , In this scenerio id is not generated.
select min,max, count(Score) from Scorelogs group by min,max
Or you can use
select ROW_NUMBER()
OVER (ORDER BY max) AS id, min,max, count(Score) from Scorelogs group by min,max
SELECT ROW_NUMBER() OVER(ORDER BY min,max) as id,
min,
max,
SUM(Score)
FROM Scorelogs
GROUP BY min,max
Fiddle Demo

sql query finding most often level appear

I have a table Student in SQL Server with these columns:
[ID], [Age], [Level]
I want the query that returns each age value that appears in Students, and finds the level value that appears most often. For example, if there are more 'a' level students aged 18 than 'b' or 'c' it should print the pair (18, a).
I am new to SQL Server and I want a simple answer with nested query.
You can do this using window functions:
select t.*
from (select age, level, count(*) as cnt,
row_number() over (partition by age order by count(*) desc) as seqnum
from student s
group by age, level
) t
where seqnum = 1;
The inner query aggregates the data to count the number of levels for each age. The row_number() enumerates these for each age (the partition by with the largest first). The where clause then chooses the highest values.
In the case of ties, this returns just one of the values. If you want all of them, use rank() instead of row_number().
One more option with ROW_NUMBER ranking function in the ORDER BY clause. WITH TIES used when you want to return two or more rows that tie for last place in the limited results set.
SELECT TOP 1 WITH TIES age, level
FROM dbo.Student
GROUP BY age, level
ORDER BY ROW_NUMBER() OVER(PARTITION BY age ORDER BY COUNT(*) DESC)
Or the second version of the query using amount each pair of age and level, and max values of count pair age and level per age.
SELECT *
FROM (
SELECT age, level, COUNT(*) AS cnt,
MAX(COUNT(*)) OVER(PARTITION BY age) AS mCnt
FROM dbo.Student
GROUP BY age, level
)x
WHERE x.cnt = x.mCnt
Demo on SQLFiddle
Another option but will require later version of sql-server:
;WITH x AS
(
SELECT age,
level,
occurrences = COUNT(*)
FROM Student
GROUP BY age,
level
)
SELECT *
FROM x x
WHERE EXISTS (
SELECT *
FROM x y
WHERE x.occurrences > y.occurrences
)
I realise it doesn't quite answer the question as it only returns the age/level combinations where there are more than one level for the age.
Maybe someone can help to amend it so it includes the single level ages aswell in the result set: http://sqlfiddle.com/#!3/d597b/9
with combinations as (
select age, level, count(*) occurrences
from Student
group by age, level
)
select age, level
from combinations c
where occurrences = (select max(occurrences)
from combinations
where age = c.age)
This finds every age and level combination in the Students table and counts the number of occurrences of each level.
Then, for each age/level combination, find the one whose occurrences are the highest for that age/level combination. Return the age and level for that row.
This has the advantage of not being tied to SQL Server - it's vanilla SQL. However, a window function like Gordon pointed out may perform better on SQL Server.

GROUP BY Function Issue

I have the below example:
SELECT name, age, location, SUM(pay)
FROM employee
GROUP BY location
This as expected will give me an error:
ORA-00979: not a GROUP BY expression
How can I get around this? I need to group by one maybe two columns but need to return all columns even if they're not used in the GROUP BY clause, I've looked at sub-queries to get around it but have had no luck so far.
You can use analytic functions:
SELECT name
, age
, location
, pay
, SUM(pay) over (partition by location order by location ) total
FROM employee
So, you can return all rows even if they are not used in the grouping.
So you want to know the total pay by location, and you want to know the names and ages of employees at each location? How about:
SELECT e.NAME,
e.AGE,
e.LOCATION,
t.TOTAL_LOCATION_PAY
FROM EMPLOYEE e
INNER JOIN (SELECT LOCATION,
SUM(PAY) AS TOTAL_LOCATION_PAY
FROM EMPLOYEE
GROUP BY LOCATION) t
ON (t.LOCATION = e.LOCATION)
Share and enjoy.
(Group b[http://docs.oracle.com/javadb/10.6.2.1/ref/rrefsqlj32654.html] Must have an aggregate function in every column that is not in the group by clause. When you are grouping, means that you want one row per group. Distinct values of the columns in the clause appear in the final result set.
This is because oracle can't know which of the values for the column that you don't have in the group by to retrieve. Consider this:
A X
B X
Select col1, col2 from myTable group by col2; -- incorrect
Select min(col1), col2 from myTable group by col2; -- correct
Why is the first incorrect? Because oracle can't know whether to retrieve A or B for the X value you have to specify it. i.e. MIN, MAX, etc.
There is an alternative to this named analytic functions that allow you to work under windows of your result set.
Now if you want total employee pay by location, and every employee you may want this.
SELECT name, age, location, SUM(pay) OVER(PARTITION BY location)
FROM employee
I believe this is better than #Bob Jarvis query as you only query the table once. Please correct me if I'm wrong. He also has employees and employee. Typo?