Unique Top 5 Random Query - sql

Let's say I have an app that determine the winners in a prize drawing. All entries are entered into a table indicating their employeeID. Each employee can enter the drawing multiple times. I select from the table, order by newid to get a random sort. I assume the more entries (database records) an employee has the better chance he will end up in the top 5 of my query each time I run it. So far so good. However, because each employee has multiple records, there is a good chance he will come up multiple times in the top 5. I need the ability to return 5 unique records from the randomly sorted results.
How do I get 5 unique rows while still ensuring those with multiple drawing entries get a heavier weighting in the selection?
My base query:
SELECT TOP 5 employeeID
FROM events
TABLESAMPLE(1000 ROWS)
ORDER BY CHECKSUM(NEWID());
Kinda what I am trying to do:
SELECT TOP 5 *
FROM events
WHERE employeeID IN (SELECT employeeID
FROM events
TABLESAMPLE(1000 ROWS)
ORDER BY CHECKSUM(NEWID())
)
ORDER BY CHECKSUM(NEWID())
But of course I cannot do an order by in the subquery.

Any solution must take into account 2 things:
If an employee enter multiple tickets, his chance of winning increases relative to other.
Everyone can only win once
Here's my approach:
;WITH
tmp1 AS
(
SELECT EmployeeID,
ROW_NUMBER() OVER (ORDER BY NEWID()) AS SortOrder
FROM Events
),
tmp2 AS
(
SELECT EmployeeID,
MIN(SortOrder) AS WinOrder
FROM tmp1
GROUP BY EmployeeID
)
SELECT TOP 5 *
FROM tmp2
ORDER BY WinOrder
The SQL Fiddle gives employees 1 & 5 higher chances to win, but they will only win once each, regardless of how many times they enter.

Here's a fairly simple way to get what you're after:
select top 5 EmployeeID
from
(
select EmployeeID, row_number() over (order by newid()) DrawOrder
from Events
) wins
group by EmployeeID
order by min(DrawOrder)

Related

MS Access TRIMMEAN how to

I need to perform TREAMMEAN in Access, which does not have this function.
In a table I have many Employees, each has many records.
I need to TRIMMEAN Values for each Employee separately.
Following queries perform TOP 10 percent for all records:
qry_data_TOP10_ASC
qry_data_TOP10_DESC
unionqry_TOP10_ASCandDESC
qry_data_ALL_minus_union_qry
After that, I can use Avg (Average).
But I don't know how to do it for each employee.
Visualization:
Note:
This question is edited to simplify problem.
You don't really give information in your pseudo code about your data fields but using your example that DOES have basic field information I can suggest the following should work as you described
It assumes field1 is your unique record ID - but you make no mention of which fields are keys
SELECT AVG(qry_data.field2) FROM qry_data WHERE qry_data.field1 NOT IN
(SELECT * FROM
(SELECT TOP 10 PERCENT qry_data.field1, qry_data.field2
FROM qry_data
ORDER BY qry_data.field2 ASC)
UNION
(SELECT TOP 10 PERCENT qry_data.field1, qry_data.field2
FROM qry_data
ORDER BY qry_data.field2 DESC)
)
This should give you what you want, the two sub-queries should correlate the TOP 10s (ascending and descending) for every employee. The two NOT INs should then remove those from the Table1 records and then you group the Employees and Average the Scores.
SELECT Table1.Employee, AVG(Table1.Score) AS AvgScore
FROM Table1
WHERE ID NOT IN
(
SELECT TOP 10 ID
FROM Table1 a
WHERE a.Employee = Table1.Employee
ORDER BY Score ASC, Employee, ID
)
AND ID NOT IN
(
SELECT TOP 10 ID
FROM Table1 b
WHERE b.Employee = Table1.Employee
ORDER BY Score DESC, Employee, ID
)
GROUP BY Table1.Employee;

How to write a sql microsoft access query that picks 20 random records out of 100 but filter based on record categories?

I need a sql query that will randomly pick 20 records from a table that contains about 100 records. Each record has an associated category that goes from 1 to 15. I want the records that are picked to be completely random. However, I can't have 3 records from the same category being picked.
It seems to me that I can randomly pick 20 records and then eliminate records which contain a given category >=3 times. And then pick again. But all these implies having more than one query. And I don't know how to pass the results of one query to another and then another in microsoft access query. The query results are supposed to serve as a control source for a form. What do i do so that just one query will give me the results which can then be used as a control source for the form?
I tried the following and the problem is that the questions from the same category are grouped together which is not what I want. Here's a sample of what I am trying.
`(SELECT TOP 3 MCQuestionsT.QuestionID, MCQuestionsT.QuestionText, MCQuestionsT.CategoryID
FROM MCQuestionsT
WHERE (((MCQuestionsT.CourseCode)="2323") AND MCQuestionsT.CategoryID = 1)
ORDER BY Rnd(MCQuestionsT.QuestionID))
UNION ALL
(SELECT TOP 3 MCQuestionsT.QuestionID, MCQuestionsT.QuestionText, MCQuestionsT.CategoryID
FROM MCQuestionsT
WHERE (((MCQuestionsT.CourseCode)="2323") AND MCQuestionsT.CategoryID = 2)
ORDER BY Rnd(MCQuestionsT.QuestionID))
UNION ALL
(SELECT TOP 3 MCQuestionsT.QuestionID, MCQuestionsT.QuestionText, MCQuestionsT.CategoryID
FROM MCQuestionsT
WHERE (((MCQuestionsT.CourseCode)="2323") AND MCQuestionsT.CategoryID = 3)
ORDER BY Rnd(MCQuestionsT.QuestionID))
`
-- example using sys.all_objects that returns three random objects of each type
SELECT type_desc, name
FROM (
SELECT type_desc, name, Id = ROW_NUMBER() OVER (PARTITION BY type_desc ORDER BY NEWID())
FROM sys.all_objects
) Q
WHERE Id < 4
-- example using your table
SELECT QuestionID, QuestionText, CategoryID
FROM (
SELECT QuestionID, QuestionText, CategoryID, Id = ROW_NUMBER() OVER (PARTITION BY CategoryID ORDER BY NEWID())
FROM dbo.MCQuestionsT
WHERE CourseCode = '2323'
) Q
WHERE Id < 4

Selecting 5 Most Recent Records Of Each Group

The below statement retrieves the top 2 records within each group in SQL Server. It works correctly, however as you can see it doesn't scale at all. I mean that if I wanted to retrieve the top 5 or 10 records instead of just 2, you can see how this query statement would grow very quickly.
How can I convert this query into something that returns the same records, but that I can quickly change it to return the top 5 or 10 records within each group instead, rather than just 2? (i.e. I want to just tell it to return the top 5 within each group, rather than having 5 unions as the below format would require)
Thanks!
WITH tSub
as (SELECT CustomerID,
TransactionTypeID,
Max(EventDate) as EventDate,
Max(TransactionID) as TransactionID
FROM Transactions
WHERE ParentTransactionID is NULL
Group By CustomerID,
TransactionTypeID)
SELECT *
from tSub
UNION
SELECT t.CustomerID,
t.TransactionTypeID,
Max(t.EventDate) as EventDate,
Max(t.TransactionID) as TransactionID
FROM Transactions t
WHERE t.TransactionID NOT IN (SELECT tSub.TransactionID
FROM tSub)
and ParentTransactionID is NULL
Group By CustomerID,
TransactionTypeID
Use Partition by to solve this type problem
select values from
(select values ROW_NUMBER() over (PARTITION by <GroupColumn> order by <OrderColumn>)
as rownum from YourTable) ut where ut.rownum<=5
This will partitioned the result on the column you wanted order by EventDate Column then then select those entry having rownum<=5. Now you can change this value 5 to get the top n recent entry of each group.

SQL - Select top 1 with according to values from two columns

I know the title doesn't say much, but let me explain you my situation:
I have the following table:
Now, I would like to select top 1 from each department, but I don't want to get duplicate position id, so I want the top employee from each department by number of projects, but distinct position ids. The results are the highlighted rows.
You cannot guarantee that the returned positions will be the best. One position might be the best in two departments, in which case, one of the results constraints will need to be relaxed.
So, here is a method to get some (perhaps all) departments with the highest ranking but distinct positions. Start by choosing only the highest ranked employees for each department. These are the one with the most projects.
Then, for each PositionTypeId choose a random department from among these alternatives. Then, for each department, choose a random position type. The following query takes this approach:
select DepID, EmplyeeID, PositionTypeId, NumProjects
from (select t.*, row_number() over (partition by DepId order by newid()) as seqnum
from (select t.*, row_number() over (partition by PositionTypeId order by newid()) as position_seqnum
from (select t.*,
dense_rank() over (partition by DepId order by NumProducts desc
) as rank_seqnum
from t
) t
where rank_seqnum = 1
) t
where position_seqnum = 1
) t
where seqnum = 1;
This is not guaranteed to return a row for each department. But, it is guaranteed that all departments returned will have different position types and the rows will be best for that department. You could probably work to tweak the middle step to ensure a greater coverage of departments. However, because the problem is not guaranteed to have a solution, such tweaks may be more effort than they are worth.

Select rows in random order and then reverse it

I need to select rows in random order and return a query which holds the rows in both regular order and in reverse order. This is done to simulate a fantasy draft for a basketball game I'm working on.
For example, I need a result set as followed:
team1 1
team2 2
team6 3
team9 4
team9 5
team6 6
team2 7
team1 8
As you can see, the first four teams are random then then following four are in reverse order.
Hope I managed to explain the problem, if not - please comment and I'll explain further.
You have to "cache" the results of the random ORDER BY.
In this code, if you refer to the CTE in the UNION it will be evaluated twice and you'll have 2 different orders. A CTE is just a macro
;WITH cList AS
(
SELECT team, ROW_NUMBER() OVER (ORDER BY NEWID()) AS rn
FROM teams
)
SELECT * INTO #tempresults FROM cList WHERE rn <= #rn --or however many
SELECT *, rn FROM #tempresults
UNION ALL
SELECT *, (2 * #rn) - rn FROM #tempresults
ORDER BY rn
Duplicating rows is easy with a dummy cross join (like this) but this requires ordering and rownumbering too over the intermediate results. I don't think it can be done in a single SQL statement
you can use a query like this:
select top(10) teamname, NewId() as Random
from teams
order by Random
this will return the top ten random teams from your database. Then you can reverse it with some code.