Stored procedure for finding score up until given date - mysql

edit: owngoals are goals made by the team total. Othergoals are goals made by other team total
I have 2 tables, a TEAMS and MATCHES table.
create table teams
(
Id char(3) primary key,
name varchar(40),
nomatches int,
owngoals int,
othergoals int,
points int
)
and
create table matches
(
id int identity(1,1),
homeid char(3) foreign key references teams(id),
outid char(3) foreign key references teams(id),
homegoal int,
outgoal int,
matchdate date
)
i have triggers for inserting and deleting that gives the correct score for the teams.
the select to show the scoreboard looks like this.
select name, nomatches, owngoals, othergoals, points from teams
order by points desc
and it will give this result, with the numbers being Number of matches, goals made by team, goals made against you, total points
Now i need to make a stored procedure that makes the scoreboard but only until a given date. I have tried some different stuff like making a copy of the Teams table as a #tmpTeams, but nothing has worked.

Do you just want an aggregation query? Your question doesn't explain what the logic is for the columns, but I'm guessing something like this:
select t.id, t.name,
sum(mh.homegoal) as homegoals,
sum(mo.outgoal) as othergoals,
coalesce(sum(mh.homegoal), 0) + coalesce(sum(mo.outgoal), 0) as totalpoints
kfrom teams t left join
matches mh
on mh.homeid = t.id and
mh.matchdate >= ? left join
matches mo
on mo.outid = t.id and
mo.matchdate >= ?
group by t.id, t.name;
The ? is intended to be a parameter for your date.
Note: I seen no reason to have triggers when this information can be calculated using a query -- unless you have a performance issue that requires triggers to solve.

Related

Update Column Using ROW_Number() function. But it is failing. Could Any one suggest a solution?

I know guys, this might be a silly question, but I have not found any solution till now, so I am asking this question with all the inputs and outputs that I have done. Could anyone provide me the solution.
What I want to do is: the parcelno can have one or more invoicenumbers, I want to find how many invoice numbers does an parcel has and give it a rank. The ranking part is important because my further work is depending on this column.
I have one table named TableA. It has three columns Invoicenumber which is the unique id, ParcelNo which can be duplicate and Ranking which I want to update.
CREATE TABLE TableA
(
Invoicenumber varchar(5),
ParcelNo varchar(5),
Ranking bit,
IDate Datetime
)
INSERT INTO TableA (Invoicenumber, ParcelNo)
VALUES ('INV01', 'P0001'), ('INV02', 'P0001'),
('INV03', 'P0002'), ('INV04', 'P0002'),
('INV05', 'P0003'), ('INV06', 'P0003')
When I run the following query the output is as desired.
;WITH CTE AS
(
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY PARCELNO ORDER BY INVOICENUMBER) AS RWNO
FROM
TableA
)
SELECT
T.*, C.RWNO
FROM CTE C
JOIN TableA T ON T.Invoicenumber = C.Invoicenumber
The output is below:
So, I tried to update the Ranking column in Table A.
I run this query to do so:
;WITH CTE AS
(
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY PARCELNO ORDER BY INVOICENUMBER) AS RWNO
FROM
TableA
)
UPDATE T
SET Ranking = C.RWNO
FROM CTE C
JOIN TableA T ON T.Invoicenumber = C.Invoicenumber
But the output is wrong. The column is not updated as expected.
Below is the output of the updated column:
Why is the Ranking column is updated incorrectly?
I want to update the column to prepare some data. This table is sample for the explanation.
I am elaborating my issue below:-
Below in the image are two tables:-
Table A and Table B has IDate column.
I want to update the IDate column in A from B. But the dates should be unique. First date should not be repeated. These date are associated with Invoicenumbers.
I think what you really want is a calculated column (called a calculated field or generated field). I'm guessing that your parcel number should point to another table that stores information about the parcels. If that's the case, then go with:
-- First approach
CREATE TABLE Parcels (
id int IDENTITY (1,1) NOT NULL,
ParcelNo varchar(5),
Description varchar(max)
-- Ranking AS (SELECT COUNT(*) FROM Invoices i WHERE i.ParcelID = id)
);
CREATE TABLE Invoices (
id int IDENTITY (1,1) NOT NULL,
InvoiceNumber varchar(5),
ParcelID int FOREIGN KEY REFERENCES Parcels(id)
);
ALTER TABLE Parcels ADD Ranking AS (SELECT COUNT(*) FROM Invoices i WHERE i.ParcelID = id);
INSERT INTO Parcels
(ParcelNo)
VALUES
('P0001'),
('P0001'),
('P0002'),
('P0003');
INSERT INTO Invoices
(InvoiceNumber, ParcelID)
VALUES
('INV01', (SELECT p.id FROM Parcels p WHERE p.ParcelNo = 'P0001')),
('INV02', (SELECT p.id FROM Parcels p WHERE p.ParcelNo = 'P0001')),
('INV03', (SELECT p.id FROM Parcels p WHERE p.ParcelNo = 'P0002')),
('INV04', (SELECT p.id FROM Parcels p WHERE p.ParcelNo = 'P0002')),
('INV05', (SELECT p.id FROM Parcels p WHERE p.ParcelNo = 'P0003')),
('INV06', (SELECT p.id FROM Parcels p WHERE p.ParcelNo = 'P0003'));
On the other hand, if you really want all the data in a single table, then try this:
-- Second approach
CREATE TABLE TableA (
Invoicenumber varchar(5),
ParcelNo varchar(5),
Ranking AS (SELECT COUNT(*) FROM TableA a WHERE a.ParcelNo = ParcelNo)
)
Some notes:
Both of my approaches assume that by ranking, you mean that you want a count of how many invoices are in a parcel.
My first approach has a circular reference, because the Invoices table has a foreign key into the Parcels table, but the Parcels table tabulates information from the Invoices table. That's why I commented out the calculated field in the first table, then added the calculated field back in after creating both tables.
Notice that I capitalized all SQL keywords (except the types such as varchar). It's easier to read SQL if you either go with all caps or no caps for an entire query.
Notice my semicolons at the end of each logical break. Semi-colons are technically optional, but a lot of folks consider using them to be good practice.
For my first approach, I'm using a foreign key. You can read more about those here.
Because my first approach split the table into 2 tables, I needed to somehow know the id of the Parcels table when populating the Invoices table, even though the ids are given by the database (so I can't know them ahead of time). Those select statements accomplish that.
My syntax should work with SQL Server, but no necessarily with any other DBMS. That's because calculated fields are not ANSI standard.

SQL query to find averages

I have created three tables such that
CREATE TABLE guest(
name varchar(100),
ranking int,
PRIMARY KEY (name)
);
CREATE TABLE room(
roomname varchar(100),
wallcolor varchar(100),
rating int,
PRIMARY KEY(roomnane)
);
CREATE TABLE reservation(
name varchar(100),
roomname varchar(100),
day varchar(100),
moveinday int,
moveoutday int,
PRIMARY KEY(roomname, day, start, finish),
FOREIGN KEY(roomname) REFERENCES room(roomname),
FOREIGN KEY(name) REFERENCES guest(name)
);
I am trying to create two queries
For each reserved room, list the average ranking of guests having
reserved that room. I am having trouble listing the average for each room. Here's what I have tried
select reservation.roomname, AVG(guest.ranking)
from reservation, room, guest
where reservation.roomname == room.roomname AND reservation.name = guest.name
This does not provide the desired result. How could I fix my query?
Query to verify that there are no conflicting reservations for rooms. (same room can't be reserved for multiple people over moveinday and moveoutday. I am not sure how to start for this
Dont forget about GROUP BY:
SELECT reservation.roomname, AVG(guest.ranking)
FROM reservation, room, guest
WHERE reservation.roomname = room.roomname
AND reservation.name = guest.name
GROUP BY reservation.roomname
You should learn to use proper join syntax and table aliases.
select r.roomname, AVG(g.ranking)
from room r join
guest g
on r.name = g.name
group by r.roomname;
In addition, the room table is not actually needed.
As for your second question, the basic should be such a join:
select room.roomname
from room
join reservation r1 on room.roomname = r1.roomname
join reservation r2 on room.roomname = r2.roomname
and then the where clause is the complicated part:
where (r1.moveinday < r2.moveinday and r1.moveoutday > r2.moveoutday)
or (r1.moveinday < r2.moveinday and r1.moveoutday > r2.moveinday)
or (r1.moveinday < r2.moveoutday and r1.moveoutday > r2.moveoutday)
The 1st case: r1 totally contains r2.
The 2nd case: r1 overlaps the move in day of r2
The 3rd case: r1 overlaps the move out day of r2
you might want to use <=, >=, depends how you consider this case. If you like to use <=, >= (instead of <. >), I recommend you add a unique ID to each reservation, as you will have to add to the where clause something such as
r1.ID <> r2.ID

Selecting fields from a table along with an aggregate of data in another table it has a one-to-many relationship with

Problem Background
This type of query seems to be my 'white whale' - I just can't wrap my head around how to do it. I have a solution below but I'm not sure if there is a better way to do things - e.g. is my solution very inefficient and likely to slow my query down a lot?
If I have a table, for example - _UserInfo - and I have a related table, for example - _UserOrders, how can I SELECT some fields from _UserInfo along with aggregates from _UserOrders?
Details / Attempts at a Solution
_UserInfo
UserID INT UNSIGNED PRIMARY_KEY
FirstName VARCHAR(50)
LastName VARCHAR(50)
_UserOrders
UserID INT UNSIGNED
OrderID INT UNSIGNED PRIMARY KEY
Cost DECIMAL(8,2)
FOREIGN KEY (UserID) REFERENCES _UserInfo(UserID)
I can very easily SELECT each user's FirstName and LastName -
SELECT
_UserInfo.FirstName AS `FirstName`,
_UserInfo.LastName AS `LastName`
FROM
_UserInfo
INNER JOIN Cities ON _UserInfo.CityID = Cities.CityID
I'd like to SUM the total Cost of each user's orders from _UserOrders and include it in my SELECT statement so that I can display FirstName, LastName, and TotalOrderCost for each user.
If the tables had a one-to-one relationship this would be a simple INNER JOIN.
INNER JOIN _UserOrders ON _UserInfo.UserID = _UserOrders.UserID
However, obviously each user can have more than one order, and I am looking for the total.
I could do this with a subquery and a JOIN, but I'm not sure if this is very, very inefficient?
SELECT
_UserInfo.FirstName AS `FirstName`,
_UserInfo.SecondName AS `LastName`,
sq.TotalOrderCost AS `TotalOrderCost`
FROM
_UserInfo
INNER JOIN
(
SELECT
UserID,
SUM(Cost) AS `TotalOrderCost`
FROM
_UserOrders
GROUP BY
UserId
) sq
ON _UserInfo.UserID = sq.UserID
Is there a more efficient way to do things? Is this a poor workaround?
(I was wondering if there is a way to do it as part of the SELECT statement, maybe?)

Database design for a Fantasy league

Here's the basic schema for my database
Table user{
userid numeber primary key,
count number
}
Table player{
pid number primary key,
}
Table user-player{
userid number primary key foreign key(user),
pid number primary key foreign key(player)
}
Table temp{
pid number primary key,
points number
}
Here's what I intend to do...
After every match the temp table is updated which holds the id of players that played the last match and the points they earned.
Next run a procedure that will match the pid from temp table with every uid of user-player table having the same pid.
add the points from temp table to the count of user table for every matching uid.
empty temp table.
My questions is considering 200 players and 10000 users,Will this method be efficient?
I am going to be using mysql for this.
People often seem to be worried about performance for small databases. Let the DBMS do what it is designed to do. If you find in practice - or preferably under load testing - that you have a performance problem, then take steps to deal with it. Otherwise don't pre-optimize!
Instead of using a temporary table to store one batch of player scores, store all player scores in a tranactional table.
Remove the user.count column and replace your temp table with something like this:
Table player_points{
pid number primary key,
match_date datetime primary key,
points number
}
With this you can easily calculate any user's score. You can even recalculate any user's score as of a given date. This is much more powerful and much simpler to maintain. Keeping a current snapshot only makes it impossible to manage should anything go wrong or should one of your users challenge their score.
This query gives you the scores for all users. You can add filters to it to do other things like finding the score for a single user or showing a leader board.
select
U.userid as UserID
, sum(S.points) as TotalScore
from user S
inner join user-player J
on S.userid = J.userid
inner join player_points S
on J.pid = S.pid
group by
U.userid
This query would give you a leader board:
select
U.userid as UserID
, sum(S.points) as TotalScore
from user S
inner join user-player J
on S.userid = J.userid
inner join player_points S
on J.pid = S.pid
group by
U.userid
order by TotalScore desc
limit 10
This query would give you points awarded to a user by date, which you could graph as-is or cumulatively, for example.
select
S.match_date as MatchDate
, sum(S.points) as TotalScore
from user-player J
inner join player_points S
on J.pid = S.pid
where J.userid = 123 -- The user ID you want.
group by
S.match_date
order by S.match_date

Please help me write a mysql query to select members of a particular department

Let's say I have 2 tables, employee and department, department has 2 departments, dpt1 and dpt2, and there are 12 employees, 6 is each department, and 4 belonging to both departments. How can I use a mysql query to find those employees who belong only to dpt2 and not dpt1?
SELECT employee.*
FROM employee
INNER JOIN employee_belongs_to_departments
WHERE employee.id = employee_belongs_to_departments.employee_id
AND employee_belongs_to_departments.department_id = 'dpt2'
AND employee.id NOT IN (
SELECT DISTINCT ebtd2.employee_id
FROM employee_belongs_to_departments ebtd2
WHERE ebtd2.employee_id = employee.id
AND ebtd2.department_id = 'dpt1'
)
The usual approach in this kind of situation would be to have a third "connection" table, called something like employeeDepartment.
CREATE TABLE employeeDepartment (
employee INT UNSIGNED NOT NULL,
department INT UNSIGNED NOT NULL,
PRIMARY KEY (employee, department)
)
and then you also make employee and department foreign keys to the primary keys of the appropriate tables, though this requires you to be using the right storage engine (not that that is hard to do).
This makes doing various things a whole lot easier than the approach you seem to be going for (storing a list of values in one column).
You could then use
SELECT
eD1.employee
FROM
employeeDepartment AS eD1
LEFT JOIN employeeDepartment AS eD2 ON
eD1.employee = eD2.employee AND
eD2.department = 'dpt2' -- replace with appropriate integer
-- if using integers as I suggested
WHERE
eD1.department = 'dpt1' AND -- same comment applies
eD2.employee IS NULL