SQL schema Site Leader Board - sql

So I am trying to set up a site which has challenges and then want to convert that to leader boards for each challenge, and then an all time leaderboard.
So I have a challenges table that looks like this:
Challenge ID Challenge Name Challenge Date Sport Prize Pool
Then I need a way so each challenge has its own leader board of say 50 people.
linked by the challenge ID where that will = Leaderboard ID
I have a leader board of 50 people for that challenge that will look something like this:
Challenge ID User Place Prize Won
My question is 2 things:
How can I make a table auto create when a new challenge is added to the challenges table?
How can I get an A site wide leader board for every challenge so it will show the following:
Rank USER Prize Money Won(total every challenge placed)
and then base rank order by how much money won..
I know this is a lot of questions all wrapped in one, schema design and logic.
Any insights greatly appreciated

A better approach than one table per challenge is one table for all of them. That way you can compute grand totals and individual challenge rankings all with the same table. You'd also want to not record the place directly but compute it on the fly with the appropriate window function depending on how you want to handle ties (rank(), dense_rank(), and row_number() will have different results in those cases); that way you don't have to keep adjusting it as you add new records.
A table something like (You didn't specify a SQL database, so I'm going to assume Sqlite. Adjust as needed.):
CREATE TABLE challenge_scores(user_id INTEGER REFERENCES users(id),
challenge_id INTEGER REFERENCES challenges(id),
prize_amount NUMERIC,
PRIMARY KEY(user_id, challenge_id));
will let you do things like
SELECT *
FROM (SELECT user_id,
sum(prize_amount) AS total,
rank() OVER (ORDER BY sum(prize_amount) DESC) AS place
FROM challenge_scores
GROUP BY user_id)
WHERE place <= 50
ORDER BY place;
for the global leaderboard, or the similar:
SELECT *
FROM (SELECT user_id,
prize_amount,
rank() OVER (ORDER BY prize_amount DESC) AS place
FROM challenge_scores
WHERE challenge_id = :some_challenge_id
GROUP BY user_id)
WHERE place <= 50
ORDER BY place;
for a specific challenge's.

Related

COUNT(*) function is returning multiple values

I am writing a specific sql query that needs to return the position of a particular entry, based on a grouped table.
Background info: I am coding a Golf Club Data Management system using Java and MS Access. In this system, the user is able to store their scores as a new entry into this table. Using this table, I have managed to extract a ranking of the top 3 Golf players, using all their recorded scores (I only used top 3 to preserve screen space).
Select TOP 3 Username, Sum(Points)
FROM Scores
GROUP By Username
ORDER BY Sum(Points) desc
This produces the required result. However, if the current user falls outside of the top 3, I want to be able to tell the user where they currently sit in the complete ranking of all the players. So, I tried to write a query that counts the number of players having a sum of points below the current user. Here is my query:
Select COUNT(*)
From Scores
GROUP BY Username
HAVING Sum(Points) < (Select Sum(Points)
FROM Scores
WHERE Username = 'Golfer210'
GROUP By Username)
This does not produce the expected number 2, but instead does this.
I have tried removing the GROUP BY function but that returns null. The COUNT DISTINCT Function refuses to work as well, and continuously returns a syntax error message, no matter how I word it.
Questions: Is there a way to count the number of entries while using a GROUP BY function? if not, is there an easier, more practical way to select the position of an entry from the grouped table? Or can this only be done in Java, after the ranking has been extracted from the database? I have not been able to find a solution anywhere
You need an additional level of aggregation:
SELECT COUNT(*)
FROM (SELECT COUNT(*)
FROM Scores
GROUP BY Username
HAVING Sum(Points) < (SELECT Sum(Points)
FROM Scores
WHERE Username = 'Golfer210'
)
) as s;
Note: You might want to check if your logic does what you expect when there are ties.

Looking for identical sequences over all users

I want to get the identical pathes (with counts) of all users
Hey everybody,
Want to keep the question short and hopefully it‘s clear what I want.
I have a table in BigQuery. There I have the following columns
- UserID
- Timestamp
- Domain
- some other columns (but I guess they are unimportant)
I have totally no idea how to fix this!
So I want to look for the same paths over all users and count how many users have the same sequence of domains.
Problem: We are talking about 129 000 users and around 5TB of data. I guess I have to limit the amount of path length or something else.
I‘m familiar with SQL but I need some help/input to keep the costs low. Every query costs money and my thought was to ask the community before I spend thousands of Dollars.
Thanks for any input!
EDIT:
I tried the following to rank the visits of domains:
SELECT
guid,
domain AS channel,
timestamp,
RANK() OVER (PARTITION BY guid ORDER BY timestamp ASC ) AS rank
FROM
data.all
My problem is now: How can I match identical pathes afters merge each "step" in this customer journey?
This may help or at least help you get started:
select domains, count(*)
from (select userid, string_agg(domain order by timestamp, ',') as domains
from t
group by userid
) u
group by domains;
I would prefer to use arrays to store the path itself, but BigQuery does not (yet) support arrays as GROUP BY keys.

SQL Server - how to select x number of task per x number of workers

I need some help writing a query in SQL Server 2012 to select a specific number of tasks per a selected number of workers. If I was doing something like this is a traditional programming language I would use something like a foreach. However I can't find a nice way to implement a foreach function into sql. I'm sure there is a simpler way to do this.
For example, lets say I have 3 tables:
MonthlyReview
DateReviewed,
WorkerID
Workers
WorkerID,
WorkerName
Tasks
TaskID,
WorkerID
First I select the workers I want to be selecting from (they are filtered on some other data such as name or org (not pictured)) so I thought it would make things easier to put it in a temp table
CREATE TABLE #WorkersToAudit (
WorkerID varchar(45),
DateReviewed datetime)
INSERT INTO #WorkersToAudit(WorkerID, DateReviewed)
SELECT TOP (4) Workers.WorkerID, MIN(MonthlyReview.DateReviewed) AS DateReviewed FROM Workers
LEFT JOIN MonthlyReview ON Workers.WorkerID = MonthlyReview.WorkerID
WHERE Workers.WorkerName LIKE '%Browne%'
GROUP BY Worker.WorkerID
DROP TABLE #WorkersToAudit
I was thinking I could then grab the (4) WokerID's in the results and find (4) TaskID's for each, but I haven't found a nice way to do this despite a lot of searching. The number of WorkerID's searched for and the number of TraderID's returned for each one can be anywhere from 1-10.
Any help would be greatly appreciated.
The easiest way to do this in one query is to use WINDOWING functions. In this case ROW_NUMBER() will work:
select WorkerID, TaskID
FROM (
SELECT Workers.WorkerID, Tasks.TaskID,
row_number() over (partition by Workers.WorkerID order by Tasks.TaskID) AS rn
FROM Workers
LEFT JOIN Tasks ON Workers.WorkerID = Tasks.WorkerID
WHERE Workers.WorkerName LIKE '%Browne%'
) where rn <= 4
Notice the key parts of the over clause of the row_number() function - the partition by essentially "groups" the counting of rows by WorkerID, and the order by specifies, well, the ordering of rows that are counted.
You can change how the row_number does its grouping and ordering, and you can include whatever columns you want in the select clause, but the key part is indeed the use of the row_number() function itself.
Good luck!

SQL Server - Variation auto increment based on primary key

my scenario is this:
Say within an engineering environment, works for a job are quoted at £5,000. However after this a variation is made an additional cost is required for £500.
now within SQL Server I have a table, lets call it Costing. This is associated to the Customer table (1 to many) 1 Customer could have many costs...
Now my question is this... is it possible to auto increment a variation number within the CostingTable, baring in mind that the CostingID is already auto incrementing or is there a different approach I can take?
Example data:
ive already done all the costing calculations etc so this is no issue, its just if it is possible to automatically add one to the VariationNumber based upon the CustomerID... thanks guys
If you want just to display it then use this:
SELECT *,
ROW_NUMBER() OVER(PARTITION BY CustomerId ORDER BY CostingId) AS Variation
FROM Costing
But if you want to store it:
;WITH MyCTE AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY CustomerId ORDER BY CostingId) AS NewVariation
FROM Costing
)
UPDATE MyCTE
SET Variation = NewVariation

Database Design for user defined groups

I'm trying to figure out the best way to design a database to support private user-defined groups. Pretty much identical to how Google Circles are. These are to be for JUST the user, much like circles are - that's why creating a user group design like I found here: https://stackoverflow.com/a/9805712/2580503 would be undesirable.
So far the only solution I can come up with is to have a table like this:
USER_ID | GROUP_ID | ARRAY(USER_ID)
Where the PKEY would actually be a compound key of (USER_ID, GROUP_ID). This way a user could have multiple groups.
Would greatly appreciate any feedback on this proposed solution and would love to hear if there is a better way to do it.
Thanks!
Edit: Just to clarify, GROUP_ID would not reference a separate table, it would just indicate the number group for that user. Also there would be a name etc. for the group as well - just wasn't necessary to include as part of the question.
This must involve at least three (3) tables if you want a normalized design. USERS, USER_GROUPS, and USER_GROUPS_MEMBERS. You are correct that the PK of USER_GROUPS would be a dyad (USER, GROUP). The PK of USER_GROUPS_MEMBERS would be a triad (USER, GROUP, USER).
What about?
Groups (GROUP_ID, USER_ID, GROUP_NAME)
Members (MEMBER_ID, GROUP_ID, USER_ID)
Although Groups might appear backwards, it actually lists the USER_ID that owns a GROUP_ID while Members gives the MEMBER_ID to which could be associated rows that have to do with this USER_ID in the given GROUP_ID.
How about
**Users**
id, name
**Groups**
id, name
**User_Groups**
id, user_id, group_id
**Group_users**
id, user_group_id, user_id
I have separated groups and user_groups assuming that there could be possibilities that you wish to have a few default groups for every user, If this is not the case, you can move the group_name directly to the user_groups and ignore the groups table