I am trying to get some statistics for an online game I maintain. I am searching for an SQL statement to get the result on the bottom.
There are three tables:
A table with teams, each having a unique identifier.
table teams
---------------------
| teamid | teamname |
|--------|----------|
| 1 | team_a |
| 2 | team_x |
---------------------
A table with players, each having a unique identifier and optionally an affiliation to one team by it's unique teamid.
table players
--------------------------------
| playerid | teamid | username |
|----------|--------|----------|
| 1 | 1 | user_a |
| 2 | | user_b |
| 3 | 2 | user_c |
| 4 | 2 | user_d |
| 5 | 1 | user_e |
--------------------------------
Finally a table with events. The event (duration in seconds) is related to one of the players through their playerid.
table events.
-----------------------
| playerid | duration |
|----------|----------|
| 1 | 2 |
| 2 | 5 |
| 3 | 3 |
| 4 | 8 |
| 5 | 12 |
| 3 | 4 |
-----------------------
I am trying to get a result where the durations of all team members is summed up.
result
--------------------------
| teamid | SUM(duration) |
|--------|---------------|
| 1 | 14 | (2+12)
| 2 | 15 | (3+8+4)
--------------------------
I tried several combinations of UNION, WHERE IN, JOIN and GROUP but could not get it right. I am using PostgreSQL and PHP. Can anyone help me?
Just use sum with group by:
select t.teamid, sum(e.duration)
from team t
join players p on t.teamid = p.teamid
join events e on p.playerid = e.playerid
group by t.teamid
If you need all teams to be returned even if they don't have events, then use an outer join instead.
Try this
SELECT teamid, Sum(duration),
AS LineItemAmount, AccountDescription
FROM teams
JOIN teams ON teams.teamid = players.teamid
JOIN events ON players.playersid = events.playersid
JOIN GLAccounts ON InvoiceLineItems.AccountNo = GLAccounts.AccountNo
GROUP BY teamid
http://www.w3computing.com/sqlserver/inner-joins-join-two-tables/
Related
I am working on a project to catalogue laptops and as such am trying to re-use as much information as possible. A simplified version of the MySQL tables are:
Table: laptop
|----------------------------------|
| id | make | line | model |
| 1 | 1 | 1 | Late 2015 13" |
|----------------------------------|
Table: make
|----------------------------------|
| id | name | other info |
| 1 | Apple | |
|----------------------------------|
Table: line
|----------------------------------|
| id | name | other info |
| 1 | MacBook Pro | |
|----------------------------------|
Table: networking
|----------------------------------|
| id | name | other info |
| 1 | A wifi card | |
| 2 | Another card | |
| 3 | Yet another | |
|----------------------------------|
Table: laptop_networking
|----------------------------------|
| id | networking | laptop |
| 1 | 3 | 1 |
| 2 | 1 | 1 |
|----------------------------------|
So far I used the current statement to retrieve the data in PHP
$statement = $dbc->prepare("
SELECT l.id
, m.id AS makeID
, m.name AS makeName
, n.id AS lineID
, n.name AS lineName
, l.model
FROM laptop l
JOIN make m
ON l.make = m.id
JOIN line n
ON l.line = n.id
WHERE l.id = :laptop);
$statement->bindParam(':laptop', $anID, PDO::PARAM_INT);
$statement->execute();
$theLaptop = $statement0>fetch();
At present running this code with $anID = 1 returns
|---------------------------------------------------------------|
| id | makeID | makeName | lineID | lineName | Model |
| 1 | 1 | Apple | 1 | MacBook Pro | Late 2015 13" |
|---------------------------------------------------------------|
What I would like to do is append another column to the table which returns all names from Networking which have an ID equal to a row in laptop_networking where the laptop field is equal to the ID from the retrieved laptop row
Such as:
|------------------------------------------------------------------------------------------|
| id | makeID | makeName | lineID | lineName | model | networking |
| 1 | 1 | Apple | 1 | MacBook Pro | Late 2015 13" | Yet another, A wifi card |
|------------------------------------------------------------------------------------------|
Is this possible as my many attempts at different types of JOINs have not yielded the desired results.
Thank you
Try this query:
SELECT laptop.id,
make.id AS makeID,
make.name AS makeName,
line.id AS lineID,
line.name AS lineName,
laptop.model,
t.networking
FROM laptop
INNER JOIN make
ON laptop.make = make.id
INNER JOIN line
ON laptop.line = line.id
INNER JOIN
(
SELECT t1.laptop, GROUP_CONCAT(t2.name) AS networking
FROM laptop_networking t1
INNER JOIN networking t2
ON t1.networking = t2.id
GROUP BY t1.laptop
) t
ON laptop.id = t.laptop
WHERE laptop.id = :laptop
Demo here:
Rextester
I have a tables like this:
Users
+----+----------+-------------+
| id | name | other_stuff |
+----+----------+-------------+
| 1 | John Doe | x |
| 2 | Jane Doe | y |
| 3 | Burt Olm | z |
+----+----------+-------------+
Places
+----+------------+-------------+
| id | name | other_stuff |
+----+------------+-------------+
| 1 | Building A | x |
| 2 | Building B | y |
+----+------------+-------------+
Subjects
+----+------------+-------------+
| id | name | other_stuff |
+----+------------+-------------+
| 1 | Math | x |
| 2 | English | y |
+----+------------+-------------+
And a joining table:
PastLectures = lectures that took place
+----+-----------+----------+------------+---------+------------+
| id | id_users | id_place | id_subjects| length | date |
+----+-----------+----------+------------+---------+------------+
| 1 | 1 | 1 | 1 | 60 | 2015-10-25 |
| 2 | 1 | 1 | 1 | 120 | 2015-11-06 |
| 3 | 2 | 2 | 2 | 120 | 2015-11-04 |
| 4 | 2 | 2 | 1 | 60 | 2015-11-10 |
| 5 | 1 | 2 | 1 | 60 | 2015-11-10 |
| 6 | 2 | 2 | 1 | 40 | 2015-11-15 |
| 7 | 1 | 2 | 2 | 30 | 2015-11-15 |
+----+-----------+----------+------------+---------+------------+
I would like to display SUM of all lessons for each user for given month. The SUM should by grouped by each Places and Subjects.
The result in final PHP output should look like this:
November 2015
+------------+-------------+---------------+-------------+
| Users.name | Places.name | Subjects.name | sum(length) |
+------------+-------------+---------------+-------------+
| Burt Olm | - | - | - |
| Jane Doe | Building B | Math | 100 |
| = | = | English | 120 |
| John Doe | Building A | Math | 120 |
| = | Building B | Math | 60 |
| = | = | English | 30 |
+------------+-------------+---------------+-------------+
I have tried creating the full output in pure SQL query using multiple GROUP BY (Group by - multiple conditions - MySQL), but when I do GROUP BY User.id,Places.id it shows each user only once (3 results) no matter the other GROUP BY conditions.
SQL:
SELECT PastLectures.id_users,Users.name AS user,Places.name AS places,Subjects.name AS subjects
FROM PastLectures
LEFT JOIN Users ON PastLectures.id_users = Users.id
LEFT JOIN Places ON PastLectures.id_Places = Places.id
LEFT JOIN Subjects ON PastLectures.id_Subjects = Subjects.id
WHERE date >= \''.$monthStart->format('Y-m-d H:i:s').'\' AND date <= \''.$monthEnd->format('Y-m-d H:i:s').'\'
GROUP BY Users.id,Places.id
ORDER BY Users.name,Places.name,Subjects.name
But I don't mind if part of the solution is done in PHP, I just don't know what to do next.
EDIT:
I also have a table Timetable, that stores who regularly teaches what and where. It stores only used combinations of the tables (each valid combination once).
Timetable = lectures that regularly take place
+----+-----------+----------+------------+-------------+
| id | id_users | id_place | id_subjects| other_stuff |
+----+-----------+----------+------------+-------------+
| 1 | 1 | 1 | 1 | x |
| 2 | 1 | 2 | 1 | y |
| 3 | 1 | 2 | 2 | z |
| 4 | 2 | 2 | 1 | a |
| 5 | 2 | 2 | 2 | b |
+----+-----------+----------+------------+-------------+
Is it possible to add only users with combinations that have a row in this table?
In this case it would mean omitting Burt Olm (no id=3 in Timetable). But if Burt has a Timetable entry and still no PastLectures entry, he would show here as in sample result (he should have had a lecture that month, because he is in Timetable, but no lectures took place).
Based on #Barmar's solution I updated the final SQL by making Timetable a primary table and adding one more LEFT JOIN to suffice those needs.
Final SQL:
SELECT Users.name AS user,Places.name AS places,Subjects.name AS subjects, SUM(PastLectures.length)
FROM Timetable
LEFT JOIN PastLectures ON PastLectures.id_users = Timetable.id_users AND PastLectures.id_place = Timetable.id_place AND PastLectures.id_subjects = Timetable.id_subjects
AND date BETWEEN '2015-11-01 00:00:00' AND '2015-11-30 23:59:59'
LEFT JOIN Places ON Timetable.id_Place = Places.id
LEFT JOIN Subjects ON Timetable.id_Subjects = Subjects.id
LEFT JOIN Users ON Timetable.id_users = Users.id
GROUP BY Timetable.id,Timetable.id_users,Timetable.id_Place,Timetable.id_Subjects
ORDER BY Users.name,Places.name,Subjects.name
You need to include Subjects.id in the GROUP BY, so you get a separate result for each subject.
Also, you shouldn't use columns in tables that are joined with LEFT JOIN in the GROUP BY column. If you do that, all the non-matching rows will be grouped together, because they all have NULL in that column. Use the columns in the main table.
GROUP BY PastLectures.id_users, PastLectures.id_Place, PastLectures.id_Subjects
DEMO
Note that there's no row for Burt Olm in the demo output, because all his rows are filtered out by the WHERE clause. If you want all users to be shown, you should make Users the main table, not PastLectures. And the date criteria needs to be moved into the ON clause when joining with PastLectures.
SELECT Users.name AS user,Places.name AS places,Subjects.name AS subjects, SUM(length)
FROM Users
LEFT JOIN PastLectures ON PastLectures.id_users = Users.id
AND date BETWEEN '2015-11-01 00:00:00' AND '2015-11-30 23:59:59'
LEFT JOIN Places ON PastLectures.id_Place = Places.id
LEFT JOIN Subjects ON PastLectures.id_Subjects = Subjects.id
GROUP BY Users.id, PastLectures.id_Place, PastLectures.id_Subjects
ORDER BY Users.name,Places.name,Subjects.name
DEMO
According to standard SQL, you should GROUP BY all the fields you select, except for the aggregated fields (like sum). Althought MySql allows to do otherwise, when it can be done adhering to the standards, it is better to do so (who knows when you need to port your code to another database engine). So write your SQL like this:
SELECT PastLectures.id_users,
Users.name AS user,
Places.name AS places,
Subjects.name AS subjects,
Sum(length)
FROM PastLectures
LEFT JOIN Users ON PastLectures.id_users = Users.id
LEFT JOIN Places ON PastLectures.id_Places = Places.id
LEFT JOIN Subjects ON PastLectures.id_Subjects = Subjects.id
WHERE date BETWEEN \''.$monthStart->format('Y-m-d H:i:s').'\'
AND \''.$monthEnd->format('Y-m-d H:i:s').'\'
GROUP BY PastLectures.id_users,
Users.name,
Places.name,
Subjects.name
ORDER BY Users.name,
Places.name,
Subjects.name
I want to join 4 tables to list all the values from a table those have the duration from last updated to current date is more that the duration in other table, table are given below (my English not good to understand so am explaining with examble)
first table daily_tasks
+---------+---------+
| task_id | type_id |
+---------+---------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
+---------+---------+
Second Table daily_task_report
+-----------+---------+------------+
| report_id | task_id | task_date |
+-----------+---------+------------+
| 1 | 1 | 2015-09-10 |
| 2 | 3 | 2015-09-10 |
| 3 | 1 | 2015-09-11 |
| 4 | 3 | 2015-09-16 |
+-----------+---------+------------+
Third Table duration_types
+---------+---------------+------------------------+
| type_id | duration_type | duration_time(in days) |
+---------+---------------+------------------------+
| 1 | Daily Task | 1 |
| 2 | Weekly Task | 6 |
| 3 | Monthly Task | 26 |
| 4 | Yearly Task | 313 |
+---------+---------------+------------------------+
Fourth Table calendar
+--------+------------+---------+
| cal_id | cal_date | holiday |
+--------+------------+---------+
| 1 | 2015-09-10 | 0 |
| 2 | 2015-09-11 | 0 |
| 3 | 2015-09-12 | 0 |
| 4 | 2015-09-13 | 1 |
+--------+------------+---------+
Here daily_tasks.type_id is from duration_types.type_id and daily_task_report.task_id is from daily_tasks.task_id. I want to select all the task_id those task_date and current_date difference will greater than duration_time, also while calculating the duration i have to avoid the dates those have holiday=1 from calendar.
I tried queries but not proper, i got the values without including the calendar table, but that not a good way, query is taking more time to execute.
"SELECT dailyTasks.task_id FROM
(SELECT tab.* FROM (SELECT
tasks.task_type,report.*
FROM daily_tasks AS tasks
LEFT JOIN daily_task_reports AS report ON tasks.task_id=report.task_id
WHERE 1 ORDER BY report.task_date DESC) as tab GROUP BY tab.d_task_id) AS dailyTasks
LEFT JOIN duration_types AS type ON dailyTasks.task_type=type.type_id
WHERE DATEDIFF(CURDATE(),dailyTasks.task_date)>=type.duration_time"
Please someone help, I stuck in this section
According to given table You have some unexpected text or unknown columns in your query
Try this query
"SELECT dailyTasks.d_task_id FROM
(SELECT tab.* FROM
(SELECT tasks.type_id,report.* FROM daily_tasks AS tasks
LEFT JOIN daily_task_reports AS report ON tasks.task_id=report.task_id
ORDER BY report.task_date DESC)
as tab GROUP BY tab.task_id) AS dailyTasks
LEFT JOIN duration_types AS type ON dailyTasks.type_id=type.type_id
WHERE DATEDIFF(CURDATE(),dailyTasks.task_date)>=type.duration_time"
It can be also works in single query
SELECT tasks.task_id FROM daily_tasks AS tasks
LEFT JOIN daily_task_reports AS report ON tasks.task_id=report.task_id
LEFT JOIN duration_types AS type ON tasks.type_id = type.type_id and DATEDIFF(CURDATE(),report.task_date) >= type.duration_time
*I tried but not exclude that id which have holiday in calendar
You can create it on your coding side I gave you new query included with calendar
*
SELECT tasks.task_id,report.task_date,calendar.holiday FROM daily_tasks AS tasks
LEFT JOIN daily_task_reports AS report ON tasks.task_id=report.task_id
LEFT JOIN duration_types AS type ON tasks.type_id = type.type_id and DATEDIFF(CURDATE(),report.task_date) >= type.duration_time
LEFT JOIN calendar ON report.task_date=calendar.cal_date
where calendar.holiday = '0'
order By report.task_date desc
I have two table 'users' and 'friends' I am having difficulty joining them
users table
id | name | usercode
--------------------
1 | david | 2WM
2 | Samme | E5N
3 | Awudu | C0Q
4 | John | VX6
5 | Jerem | FG3
Friends Table
id | actor | target
--------------------
1 | E5N | FG3
2 | 2WM | VX6
3 | FG3 | 2WM
4 | C0Q | VX6
5 | FG3 | VX6
Basically i want to select all users from USERS table who has 'FG3' in either target or actor column in the FRIENDS table.
The result will be
id | name | usercode | actor | target
--------------------------------------
2 | Samme | E5N | E5N | FG3
1 | david | 2WM | FG3 | 2WM
5 | John | VX6 | FG3 | VX6
I have triend everything i know but still i am not getting the correct results
I will be glad if anyone can help me since I need to present this work tomorrow morning. Thank you
Looks like you want to join on usercode equals actor or target, then put the 'FG3' part in a WHERE clause:
SELECT users.id, users.name, users.usercode, friends.actor, friends.target
FROM users
INNER JOIN friends
ON users.usercode = friends.actor OR users.usercode = friends.target
WHERE users.usercode != 'FG3'
AND (friends.actor = 'FG3' OR friends.target = 'FG3');
Using INNER JOIN limits your query to only records that exist in both tables.
I have 4 tables that I need to pull data from. I need to count how many people are signed for a single event and see if a user is applied for an event.
These are my table setups:
TABLE: users
+----+----------+-------+--------+-------+
| id | username | level | class | guild |
+----+----------+-------+--------+-------+
| 1 | example1 | 100 | Hunter | blah |
| 2 | example2 | 105 | Mage | blah2 |
| 3 | example3 | 102 | Healer | blah |
+----+----------+-------+--------+-------+
ID is primary
TABLE: event_randoms
+----+----------+-------+--------+----------+----------+
| id | username | level | class | apped_by | event_id |
+----+----------+-------+--------+----------+----------+
| 1 | random1 | 153 | Hunter | 3 | 3 |
| 2 | random2 | 158 | Healer | 3 | 1 |
| 3 | random3 | 167 | Warrior| 1 | 3 |
+----+----------+-------+--------+----------+----------+
ID is primary
apped_by should be foreign key to users.id
event_id should be foreign key to events.id
TABLE: events
+----+------------+------------+-----------+-----------+-----------+
| id | event_name | event_date | initiator | min_level | max_level |
+----+------------+------------+-----------+-----------+-----------+
| 1 | event1 | date1 | 1 | 100 | 120 |
| 2 | event2 | date2 | 1 | 121 | 135 |
| 3 | event3 | date3 | 1 | 100 | 120 |
| 4 | event4 | date4 | 1 | 150 | 200 |
+----+------------+------------+-----------+-----------+-----------+
ID is primary
TABLE: event_apps
+----+----------+--------------+
| id | event_id | applicant_id |
+----+----------+--------------+
| 1 | 3 | 2 |
| 2 | 4 | 2 |
| 3 | 3 | 1 |
| 4 | 1 | 3 |
+----+----------+--------------+
ID is primary
event_id should be foreign key to events.id
applicant_id should be foreign key to users.id
I will be the first to admit that I am very new to this. I just learned how to use MySQL a few days ago. I can grab stuff from a single table, but I am unsure how to grab from multiple tables.
This is the SQL query I tried
SELECT DD_events.id, event_id, applicant_id, guild, level, class, DD_users.id
FROM DD_events, DD_event_apps, DD_users
WHERE DD_event_apps.event_id = DD_events.id
AND DD_event_apps.applicant_id = DD_users.id
and tried to print_r an array but the array turns up empty.
So a few questions pertain to this:
1: How would I count and display as a number how many people (users and randoms) are signed up for an event?
eg: event 3 should have 4 total (2 users and 2 randoms)
2: How do I see if a particular individual is signed for an event and display text based if they are or not?
eg: user 1 is signed up for event 3 so it would be "Registered" but user 2, who is not signed, would display "Not Registered"
3: I want to display info for who is signed for a particular event in 2 tables, 1 for users and another for randoms.
eg: Event 3 would have 2 users info (username, guild, class, level) under the users table and then 2 random users info (name, class, level, what user applied this person) in the random table.
Any and all help is appreciated even if you can answer 1 part.
I'm thinking this would be your base query:
SELECT
event.id,
app.applicant_id,
usr.guild,
usr.level,
usr.class,
usr.id AS Userid
FROM
DD_events event
JOIN
DD_event_apps app
ON (event.id = app.event_id)
LEFT JOIN
DD_users usr
ON (app.user_id = usr.id)
You can make modifications to this to aggregate it, like so:
SELECT
event.id,
COUNT(app.applicant_id) AS ApplicantCount,
COUNT(DISTINCT usr.guild) AS UniqueGuilds,
COUNT(DISTINCT usr.level) AS UniqueLevels,
COUNT(DISTINCT usr.class) AS UniqueClasses,
COUNT(DISTINCT usr.id) AS UniqueUsers
FROM
DD_events event
JOIN
DD_event_apps app
ON (event.id = app.event_id)
LEFT JOIN
DD_users usr
ON (app.user_id = usr.id)
GROUP BY
event.id
I could write those scripts for you, but I think this provides a good starting point for you to continue from. You'll find that T-SQL is fairly simple when you are trying to get the results you are looking for. Hope this helps!
<?php $query = "SELECT count(*) AS numbuh FROM DD_event_apps WHERE event_id = {$row['id']}";
try
{
// These two statements run the query against your database table.
$stmt = $db->prepare($query);
$stmt->execute();
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
echo($query);
// Finally, we can retrieve all of the found rows into an array using fetchAll
$count = $stmt->fetchAll();
echo($count['numbuh']); ?>