How to pull top 3 per group in access SQL - sql

I'm attempting to pull the top three performers in a group. I'm not sure how to do this in Access, what I need is for each group to pull the top three.
http://allenbrowne.com/subquery-01.html
I've tried these subqueries however they give me an error message. Is there a better way to do this?
It's for keyword performance.
Table fields are
Keyword | Campaign | Ad Group | Clicks | Impressions
I want the top 3 for clicks for each ad group. I want top 3 impressions too but once I know how to do clicks I'll be able to modify it as needed.
I'm trying to get like
Ad Group 1 - First Top
Ad Group 1 - Second Top
Ad Group 1 - Third Top
Ad Group 2 - First Top
Ad Group 2 - Second Top
Ad Group 2 - Third Top
etc
SELECT KeywordReport.ID, KeywordReport.Campaign, KeywordReport.[Ad group], KeywordReport.Keyword, KeywordReport.Clicks, KeywordReport.Impressions, KeywordReport.[Avg CPC], KeywordReport.[Search Impr share], KeywordReport.Cost
FROM KeywordReport
WHERE (((KeywordReport.ID) In (SELECT TOP 3 ID
FROM KeywordReport AS Dupe
WHERE Dupe.ID = KeywordReport.ID
ORDER BY Dupe.Clicks DESC)))
ORDER BY KeywordReport.[Ad group];
It is returning all entries not the top 3.

I think you were close:
WHERE (((KeywordReport.ID) In (SELECT TOP 3 ID
FROM KeywordReport AS Dupe
WHERE Dupe.[Ad Group] = KeywordReport.[Ad Group] <-- change this line
ORDER BY Dupe.Clicks DESC)))

Related

How to get Top 2 dates for each ID in MS Access? [duplicate]

I am making some software that tracks the scores of a test. There are multiple users, the details of which are stored in a user table. There is then a progress table which tracks a score with the date and the user who's score it is.
I can already select the 3 most recent records for a chosen userID
SELECT TOP 3 Progress.LoginID, Progress.Score, Progress.[Date Taken]
FROM Progress
WHERE (((Progress.LoginID)=[Enter LoginID:]))
ORDER BY Progress.[Date Taken] DESC;
And I can show all the records grouped by LoginID
SELECT Progress.LoginID, Progress.Score, Progress.[Date Taken]
FROM Progress
GROUP BY Progress.LoginID, Progress.Score, Progress.[Date Taken];
I want to be able to show the 3 most recent records for each user in one query and I'm unsure of how to use nested queries/subqueries to do so.
The field names for the user table are:
LoginID
Forename
Surname
DOB
Guardian Forename
Guardian Surname
Telephone Number
The field names for the progress table are:
ProgressID
LoginID
Score
Date Taken
Any help would be appreciated.
I had a similar problem a year ago: Top 3 per group including 0
Using the same approach, this will return the latest three dates for each LoginID - you may get more than three records if there are tied dates for the same LoginID.
SELECT PR1.LogInID, PR1.Score, PR1.[Date Taken]
FROM Progress AS PR1
WHERE PR1.[Date Taken] IN (
SELECT TOP 3 PR2.[Date Taken]
FROM Progress PR2
WHERE PR2.LoginID = PR1.LoginID
ORDER BY PR2.[Date Taken] DESC
)
ORDER BY LoginID, [Date Taken]
You can put your statement without top ranking into brackets and do your ranking in a second step simple as that:
SELECT Top 3 step1.*
FROM (<YOUR STATEMENT>) AS step1
The question is older but it might be a solution though.

How to consecutively count everything greater than or equal to itself in SQL?

Let's say if I have a table that contains Equipment IDs of equipments for each Equipment Type and Equipment Age, how can I do a Count Distinct of Equipment IDs that have at least that Equipment Age.
For example, let's say this is all the data we have:
equipment_type
equipment_id
equipment_age
Screwdriver
A123
1
Screwdriver
A234
2
Screwdriver
A345
2
Screwdriver
A456
2
Screwdriver
A567
3
I would like the output to be:
equipment_type
equipment_age
count_of_equipment_at_least_this_age
Screwdriver
1
5
Screwdriver
2
4
Screwdriver
3
1
Reason is there are 5 screwdrivers that are at least 1 day old, 4 screwdrivers at least 2 days old and only 1 screwdriver at least 3 days old.
So far I was only able to do count of equipments that falls within each equipment_age (like this query shown below), but not "at least that equipment_age".
SELECT
equipment_type,
equipment_age,
COUNT(DISTINCT equipment_id) as count_of_equipments
FROM equipment_table
GROUP BY 1, 2
Consider below join-less solution
select distinct
equipment_type,
equipment_age,
count(*) over equipment_at_least_this_age as count_of_equipment_at_least_this_age
from equipment_table
window equipment_at_least_this_age as (
partition by equipment_type
order by equipment_age
range between current row and unbounded following
)
if applied to sample data in your question - output is
Use a self join approach:
SELECT
e1.equipment_type,
e1.equipment_age,
COUNT(*) AS count_of_equipments
FROM equipment_table e1
INNER JOIN equipment_table e2
ON e2.equipment_type = e1.equipment_type AND
e2.equipment_age >= e1.equipment_age
GROUP BY 1, 2
ORDER BY 1, 2;
GROUP BY restricts the scope of COUNT to the rows in the group, i.e. it will not let you reach other rows (rows with equipment_age greater than that of the current group). So you need a subquery or windowing functions to get those. One way:
SELECT
equipment_type,
equipment_age,
(Select COUNT(*)
from equipment_table cnt
where cnt.equipment_type = a.equipment_type
AND cnt.equipment_age >= a.equipment_age
) as count_of_equipments
FROM equipment_table a
GROUP BY 1, 2, 3
I am not sure if your environment supports this syntax, though. If not, let us know we will find another way.

Top n records per group sql in access

I am making some software that tracks the scores of a test. There are multiple users, the details of which are stored in a user table. There is then a progress table which tracks a score with the date and the user who's score it is.
I can already select the 3 most recent records for a chosen userID
SELECT TOP 3 Progress.LoginID, Progress.Score, Progress.[Date Taken]
FROM Progress
WHERE (((Progress.LoginID)=[Enter LoginID:]))
ORDER BY Progress.[Date Taken] DESC;
And I can show all the records grouped by LoginID
SELECT Progress.LoginID, Progress.Score, Progress.[Date Taken]
FROM Progress
GROUP BY Progress.LoginID, Progress.Score, Progress.[Date Taken];
I want to be able to show the 3 most recent records for each user in one query and I'm unsure of how to use nested queries/subqueries to do so.
The field names for the user table are:
LoginID
Forename
Surname
DOB
Guardian Forename
Guardian Surname
Telephone Number
The field names for the progress table are:
ProgressID
LoginID
Score
Date Taken
Any help would be appreciated.
I had a similar problem a year ago: Top 3 per group including 0
Using the same approach, this will return the latest three dates for each LoginID - you may get more than three records if there are tied dates for the same LoginID.
SELECT PR1.LogInID, PR1.Score, PR1.[Date Taken]
FROM Progress AS PR1
WHERE PR1.[Date Taken] IN (
SELECT TOP 3 PR2.[Date Taken]
FROM Progress PR2
WHERE PR2.LoginID = PR1.LoginID
ORDER BY PR2.[Date Taken] DESC
)
ORDER BY LoginID, [Date Taken]
You can put your statement without top ranking into brackets and do your ranking in a second step simple as that:
SELECT Top 3 step1.*
FROM (<YOUR STATEMENT>) AS step1
The question is older but it might be a solution though.

Combining 1:M tables with removing duplicate fields

Is it possible to display 2 column with no direct relationship side by side without a 'product' (column1xcolumn2)?
check this. http://sqlfiddle.com/#!3/212b6/1
I am trying create a query that displays all group_id,website and History with minimum duplicate fields. I have 3 tables- Group, Website and History but it displays cartesian join. If I have 4 websites and 3 History for 1 group. It displays 12 records. I want something lik this :
group_id | website | History
1 website1 hist1
1 website2 hist2
1 website3 hist3
You can do this in most databases by using row_number() to assign a row number. This is ANSI standard functionality. To come close to what you want:
select g.group_id, w.website, h.ma_history
from (select g.*, row_number() over (order by group_id) as seqnum
from tbl_group g
) g full outer join
(select w.*, row_number() over (order by website) as seqnum
from table_website w
) w
on g.seqnum = w.seqnum full outer join
(select h.*, row_number() over (order by ma_history) as seqnum
from table_ma_history h
) h
on h.seqnum = coalesce(g.seqnum, w.seqnum)
The difference from your desired representation is that the "missing" values are not replicated from one row to the next. Instead, they are represented as NULL. For your example:
group_id | website | History
1 website1 hist1
NULL website2 hist2
NULL website3 hist3
Since you say "something like", is this sufficiently close? Replicating the values is easier or harder depending on the database.

Using count I'm only getting one row back

I have a table like the following
User Item
A 1
A 1
A 1
B 1
B 2
C 2
C 2
A 2
I'm trying to run a query so I can get an output such as
User Item Count
A 1 3
B 1 1
B 2 1
A 2 1
C 2 2
I've tried the following query, however I'm not getting the output right.
select f.item,f.uid, COUNT(f.uid) as count
from fresh f,
product p
where f.locationid = p.iid
group by f.locationid, f.uid
order by f.uid desc;
Can anyone point out how I write a query to get the required output? I could write it up in python / ruby but I think it'll take a lot longer to run! :(
Select user, Item, count(*) Count From tablename
Group by User, Item
Order by Item
SELECT user, item, count(*) FROM fresh GROUP BY user, item
Why are you joining to the product table, when it is not used in the results?
That is likely cause of the issue, since it is an inner join, when there isn't a match, there will be no result, so you'll lose data, try just:
select
item,
uid,
COUNT(*) as count
from
fresh
group by
locationid,
uid
order by
uid desc
;