I'm trying to get a record from a table that has the minimum value of a specific column, but I want the rest of the data in that record without grouping one of the columns.
This is sample data:
I want a query that will return:
January, 24, 0, 3February, 12, 0, 1
One option is to use analytic function which "ranks" data; then fetch rows that rank as the highest. Something like this:
Sample data:
SQL> with test (month, day, c_initial, ending) as
2 (select 'jan', 24, 0, 3 from dual union all
3 select 'jan', 24, 1, 6 from dual union all
4 select 'jan', 24, 2, 2 from dual union all
5 select 'feb', 12, 0, 1 from dual union all
6 select 'feb', 12, 1, 6 from dual union all
7 select 'feb', 12, 2, 5 from dual
8 ),
Query begins here:
9 temp as
10 (select t.*,
11 row_number() over (partition by month, day order by c_initial) rn
12 from test t
13 )
14 select month, day, c_initial, ending
15 from temp
16 where rn = 1;
MON DAY C_INITIAL ENDING
--- ---------- ---------- ----------
feb 12 0 1
jan 24 0 3
SQL>
If there are ties, then consider RANK instead.
Also, initial is invalid column name so I renamed it to c_initial.
#Littlefoot gave probably the only reasonable answer. However, if your table is very large and performance is an issue, this version might be more efficient:
select month,
day,
min(c_initial) keep ( dense_rank first order by c_initial asc ) c_initial,
min(ending) keep ( dense_rank first order by c_initial asc ) ending
from your_table
group by month, day;
The min() keep tells Oracle you just want the value for which ever row in each group has the lowest value for c_initial. So, it can discard the values it knows you're not interested in, rather than keeping them around, sorting them, and then giving you the first one.
Incidentally, what tells Oracle to return the data for the row having this lowest c_initial value is the dense_rank first order by c_initial asc clause and NOT the min() function. Using a max() function would give the exact same answer. I use min() is just a style thing -- it reminds me that I'm looking for the first row in the group.
I have a table (books) like this:
id | author | book
--------------
1, Joy, book1
2, Joy, book2
3, Bob, book3
4, Bob, book4
5, Bob, book5
6, Dan, book6
...
I need a query to get 10 authors with their books. The below select is NOT what I want:
SELECT author, book
FROM books
LIMIT 10
How can I limit the result in 10 authors?
here is one way :
select * from (
select * , dense_rank() over (order by author) rn
from books
) t where t.rn < 11
As a note: If these are your only columns, then you might consider aggregation:
select author, array_agg(book)
from books
group by author
limit 10;
This returns 10 rows rather than a variable number of rows.
I have a table that holds the results of a survey:
submitter issue q1 q2 q3 q4 q5
mike 11557 4 3 4 5 1
mark 13554 5 5 5 5 5
luke 15110 1 1 1 1 1
luke 15110 1 1 1 1 1
donald 16900 4 2 2 4 5
joe 11562 5 5 5 5 5
joe 11562 5 5 5 5 5
sam 12485 2 3 4 3 4
sam 12485 2 3 4 3 4
sam 12485 2 3 4 3 4
I want to be able to filter out multiple submissions and count only 1 of them.
Some folks submitted 3 or 4 times.
I know how to find out how many times a survey was submitted and by whom:
SELECT
submitter
,issue
,COUNT(*) as '# of times Survey submitted'
FROM
Survey
GROUP BY
submitter, issue
HAVING
COUNT(*) > 1
But, I'm not sure how I can use this query to filter out the multiple submissions.
The current query I am working with is:
SELECT 'Question #1' as 'Survey Question'
,CAST(CAST(SUM(q1) AS float)/COUNT(q1) AS decimal (4,2)) as 'Average Score'
FROM Survey
WHERE COALESCE(q1,q2,q3,q4,q5) IS NOT NULL
UNION ALL
SELECT 'Question #2' as 'Survey Question'
,CAST(CAST(SUM(q2) AS float)/COUNT(q2) AS decimal (4,2)) as 'Average Score'
FROM Survey
WHERE COALESCE(q1,q2,q3,q4,q5) IS NOT NULL
UNION ALL
etc...
The desired outcome is: (Note: this result set is not accurate. Just format i would like to have.)
Survey Question Average Score
Question #1 4.58
Question #2 4.80
Question #3 4.60
Question #4 4.59
Question #5 4.64
Can anyone provide a clue?
Thanks so much!
I think I got the math right, but my results don't match yours exactly. Are you sure your desired results are correct?
DECLARE #yourTable TABLE (submitter VARCHAR(10), Issue INT, q1 TINYINT, q2 TINYINT,q3 TINYINT, q4 TINYINT,q5 TINYINT);
INSERT INTO #yourTable
VALUES ('mike',11557,4,3,4,5,1),
('mark',13554,5,5,5,5,5),
('luke',15110,1,1,1,1,1),
('luke',15110,1,1,1,1,1),
('donald',16900,4,2,2,4,5),
('joe',11562,5,5,5,5,5),
('joe',11562,5,5,5,5,5),
('sam',12485,2,3,4,3,4),
('sam',12485,2,3,4,3,4),
('sam',12485,2,3,4,3,4);
WITH CTE_Distinct
AS
(
SELECT DISTINCT *
FROM #yourTable --just change this to your actual table name.
)
SELECT REPLACE(question,'q','Question #') AS [Survey Question],
CAST(AVG(val * 1.0) AS DECIMAL(4,2)) AS [Average Score]
FROM CTE_Distinct
UNPIVOT
(
val FOR question IN (q1,q2,q3,q4,q5)
) unpvt
GROUP BY question
Results:
Survey Question Average Score
-------------------- ---------------------------------------
Question #1 3.50
Question #2 3.17
Question #3 3.50
Question #4 3.83
Question #5 3.50
WITH TestData AS (
SELECT *
FROM (VALUES
('Mike', 11557, 4, 3, 4, 5, 1)
, ('Mark', 13554, 5, 3, 5, 5, 5)
, ('Luke', 15110, 1, 1, 1, 1, 1)
, ('Luke', 15110, 1, 1, 1, 1, 1)
, ('Donald', 16900, 4, 2, 2, 4, 5)
, ('Joe', 11562, 5, 5, 5, 5, 5)
, ('Joe', 11562, 5, 5, 5, 5, 5)
, ('Sam', 12485, 2, 3, 4, 3, 4)
, ('Sam', 12485, 2, 3, 4, 3, 4)
, ('Sam', 12485, 2, 3, 4, 3, 4)
) A (Submitter, Issue, Q1, Q2, Q3, Q4, Q5)
)
SELECT SurveyQuestion
, AverageScore = AVG(QuestionAnswer * 1.) -- Change the math here if this isn't what you want
FROM (
SELECT A.Submitter
, A.Issue
, B.SurveyQuestion
, B.QuestionAnswer
, RowNum = ROW_NUMBER() OVER(PARTITION BY A.Submitter, A.Issue, B.SurveyQuestion ORDER BY (SELECT NULL)) -- Replace ORDER BY (SELECT NULL) with something more meaningful if you can
FROM TestData A
CROSS APPLY(VALUES -- Unpivot
('Question #1', A.Q1)
, ('Question #2', A.Q2)
, ('Question #3', A.Q3)
, ('Question #4', A.Q4)
, ('Question #5', A.Q5)
) B (SurveyQuestion, QuestionAnswer)
WHERE B.SurveyQuestion IS NOT NULL
) A
WHERE RowNum = 1
GROUP BY SurveyQuestion;
The first solution I think you can apply is: pick the submitter and issue and the max value of every one of the answers given per per sumitter:
select submitter, issue,
(select max(q1)
from survey
where submitter = parent.submitter
and issue = parent.issue) as q1,
(select max(q2)
from survey
where submitter = parent.submitter
and issue = parent.issue) as q2,
(select max(q3)
from survey
where submitter = parent.submitter
and issue = parent.issue) as q3,
(select max(q4)
from survey
where submitter = parent.submitter
and issue = parent.issue) as q4,
(select max(q5)
from survey
where submitter = parent.submitter
and issue = parent.issue) as q5
from survery as parent
group by submitter, issue;
But the problem of this solution is that it gives for example the greatest answers per question, which might not be the desired output.
Another approach passes by adding an id per register:
alter table survery add id bigint auto_increment;
With an id that marks every line as different, this is fish of another keetle. The select is much, much simpler:
select *
from survey
where (submitter, issue, id ) in
(
select submitter, issue, max(id)
from survey
group by submitter, issue);
The inner select (the one that haves the group by), identifies what id you want to get, the second select retrieves all the information: submitter, id, and the answers. You can use it with a max() to retrieve the last answer as the good answer, or you can use it with a min() to retrieve the first answer.
Update
Sorry, I din't read that "average" request you made. In the case you want an average value instead of the answer, I humbly recomend the second approach. The select would be then:
select avg(q1) as avg_q1,
avg(q2) as avg_q2,
....
from survey
where (submitter, issue, id ) in
(
select submitter, issue, max(id)
from survey
group by submitter, issue);
I have a database structure like this:
Table - lodges
LodgeID (PK)
Lodge
etc
Table - scores
ScoreID (PK)
Score
CategoryID
LodgeID (FK)
I'm trying to return results in the form:
LodgeID, Lodge, Category, Number of Scores in that Category, Average Score in that Category
So for example, if I had:
lodges
LodgeID, Lodge
1, Lodge One
2, Lodge Two
scores
ScoreID, Score, CategoryID, LodgeID
1, 3, 101, 1
2, 5, 101, 1
3, 7, 101, 1
4, 10, 102, 2
5, 20, 102, 2
6, 30, 102, 2
7, 40, 102, 2
I'd like to return:
1, Lodge One, 3, 5
2, Lodge Two, 4, 25
I've been trying things like:
SELECT COUNT(ScoreID) as scoreCount, AVG(Score) as AverageScore, Lodge
FROM scores_temp
INNER JOIN lodges_temp ON scores_temp.LodgeID = lodges_temp.LodgeID
SELECT lodges_temp.LodgeID, Lodge, COUNT(ScoreID) as scoreCount, AVG(Score) as AverageScore FROM lodges_temp INNER JOIN scores_temp ON lodges_temp.LodgeID = scores_temp.LodgeID
Without any success. Any pointers would be much appreciated.
Try this
SELECT COUNT(ScoreID) as scoreCount, AVG(Score) as AverageScore, Lodge
FROM scores_temp
INNER JOIN lodges_temp ON scores_temp.LodgeID = lodges_temp.LodgeID
GROUP BY Lodge
You are missing a group by clause:
SELECT COUNT(ScoreID) as scoreCount, AVG(Score) as AverageScore, Lodge
FROM scores
INNER JOIN lodges ON scores.LodgeID = lodges.LodgeID
GROUP BY lodge
I have a table with
cID, side, row, column
with some data of
24, 1, 10, 5
25, 1, 12, 6
24, 2, 18, 3
and so on. Now I want these data to be show in the form of:
cID=24
side 1 2
row 10 18
column 5 3
cID=25
side 2
row 12
column 6
The cID is filtered in the query so the output will be the 3 rows (side, row, column) and the data of them of a specific cID.
Is that possible with MsAccess Query/SQL and how?
Thanks!
Something on these lines:
TRANSFORM First(q.rvalue) AS firstofrow
SELECT q.rhead
FROM (SELECT cid,
side,
row AS rvalue,
"row" AS rhead
FROM atable
UNION ALL
SELECT cid,
side,
column AS rvalue,
"column" AS rhead
FROM atable) AS q
WHERE q.cid = 24
GROUP BY q.rhead
PIVOT q.side;