Mysql, one to many relationship, need most recent - php

It's a long time since I've worked with MySQL and I'm learning fast, with a lot of help from stackoverflow along the way. This one has me stumped though. I'm certain I've done something similar in the past but despite trying a few things I haven't achieved it yet. I suspect last time I had to utilise PHP code to ignore duplicate rows but I'm not so sure.
I am working with two tables. Table 1 has a unique record for every year, and a reference to a dept. Table 2 stores the head of each dept, and the year they were appointed.
Table 1 Table 2
Year | Dept Dept | Head | Since Year
1982 | 1 1 | Hutchinson | 1979
1983 | 2 1 | Holroyd | 1983
1984 | 2 1 | Farrey | 1987
1985 | 2 2 | Mainwaring | 1983
1986 | 1 2 | Blanche | 1985
1987 | 2 3 | Sunt | 1986
1988 | 3
What I need is a query that returns the person in charge of the referenced dept at the year in question. Like this:
1982 | 1 | Hutchinson
1983 | 2 | Mainwaring
1984 | 2 | Mainwaring
1985 | 2 | Blanche
1986 | 1 | Holroyd
1987 | 2 | Blanche
1988 | 3 | Sunt
MySQL has moved on a fair bit since I last used it. Sub-queries weren't a thing back then and I'm thinking they might help here? Any advice appreciated.

You don't have to use subquery.
Just select valid Heads twice and then discard those where there exists Head appointed later.
SELECT t1.*,t2.Head
FROM Table1 t1
JOIN Table2 t2 ON t1.Dept = t2.Dept AND t1.Year >= t2.`Since Year`
LEFT JOIN Table2 t22 ON t2.Dept = t22.Dept AND t1.Year >= t22.`Since Year` AND t2.`Since Year` < t22.`Since Year`
WHERE t22.Head IS NULL
t2.`Since Year` < t22.`Since Year` find Heads appointed later than current Head
WHERE t22.Head IS NULL select only Heads without successor
Demo

Seeking information about joins
SELECT T1.Year, T1.Dept, T2.Head
FROM Table 1 AS T1
INNER JOIN Table 2 AS T2 ON T1.Dept=T2.Dept

Related

Select MySQL return table header as well table body in one query

Hello I am facing hard time trying to realized this task. The problem is that I am not sure in which way this have to be proceeded and couldn't find tutorials or information about realizing this type of task.
The question is I have 2 tables and one connecting table between the two of them. With regular query usually what is displayed is the table header which is known value and them then data. In My case I have to display the table horizontally and vertically since the header value is unknown value.
Here is example of the DB
Clients:
+--------+------ +
| ID | client|
+--------+------ +
| 1 | Sony |
| 2 | Dell |
+--------+------ +
Users:
+--------+---------+------------+
| ID | name | department |
+--------+--------+-------------+
| 1 | John | 1|
| 2 | Dave | 2|
| 3 | Michael| 1|
| 4 | Rich | 3|
+--------+--------+-------------+
Time:
+--------+------+---------------------+------------+
| ID | user | clientid | time | date |
+--------+------+---------------------+------------+
| 1 | 1 | 1 | 01:00:00 | 2017-01-02 |
| 2 | 2 | 2 | 02:00:00 | 2017-01-02 |
| 3 | 1 | 2 | 04:00:00 | 2017-02-02 | -> Result Not Selected since date is different
| 4 | 4 | 1 | 02:00:00 | 2017-01-02 |
| 5 | 1 | 1 | 02:00:00 | 2017-01-02 |
+--------+------+---------------------+------------+
Result Table
+------------+--------+-----------+---------+----------+
| Client | John | Michael | Rich | Dave |
+------------+--------+-----------+---------+----------+
| Sony |3:00:00 | 0 | 2:00:00 | 0 |
+------------+--------+-----------+---------+----------+
| Dell | 0 | 0 | 0 | 2:00:00 |
+------------+--------+-----------+---------+----------+
First table Clients Contains information about clients.
Second table Users Contains information about users
Third Table Time contains rows of time for each users dedicated to different clients from the clients table.
So my goal is to make a SQL Query which will show the Result table. In other words it will select sum of hours which every user have completed for certain client. The number of clients and users is unknown. So first thing that have to be done is Select all users, no matter if they have hours completed or not. After that have to select each client and the sum of hours for each client which was realized for individual user.
The problem is I don't know how to approach this situation. Do I have first to make one query slecting all users then foreach them in the table header and then realize second query selecting the hours and foreaching the body conent, or this can be made with single query which will render the whole table.
The filters for select command are:
WHERE MONTH(`date`) = '$month'
AND YEAR(`date`) ='$year'
AND u.department = '$department'
Selecting single row for tume SUM is:
(SELECT SUM( TIME_TO_SEC( `time` ) ) FROM Time tm
WHERE tm.clientid = c.id AND MONTH(`date`) = '$month' AND YEAR(`date`) ='$year'
This is the query to select the times for a user , here by my logic this might be transformed with GROUP BY c.id (client id), and the problem is that it have to contains another WHERE clause which will specify the USER which is unknown. If the users was known value was for example 5, there is no problem to make 5 subsequent for each user WHERE u.id = 1, 2, 3 etc.
So here are the 2 major problems how to display in same query The users header and them select the sum of hours for each client corresponding the user.
Check out the result table hope to make the things clear.
Any suggestion or answer which can come to resolve this situation will be very helpful.
Thank you!

Inner Join duplicates

I have tried to implement it with this tutorial: http://www.w3schools.com/sql/sql_join_inner.asp It is working, but I get duplicate entries. I have 3 tables and I would like to get only 2 columns. One for name and one for date. For 1 name is more date in the table. I would like to get only 1 date per name, only today.
EDIT:
The result is now looks like this:
Name Date
---- -----------
XY 2014-07-25
XY 2014-07-26
XY 2014-07-29
Z 2014-07-09
Z 2014-07-29
What I would like to get:
Name Date
---- --------
XY 2014-07-29
Z 2014-07-29
To get only the name and the date you need to use the SELECT statement (http://www.w3schools.com/sql/sql_select.asp)
INNER JOIN doesn't remove duplicates. To do that you need to use the DISTINCT statement
(http://www.w3schools.com/sql/sql_distinct.asp)
Example of your problem (NOTE: Tables are fake and might not make sense, they're just for the example)
TABLE1 TABLE2
|id | date | |id | name | age |
| 1 | 2010 | | 1 | mike | 5 |
| 2 | 2010 | | 2 | mike | 6 |
Since you have 2 people called mike that have the same date, if you join the tables you will get:
enter code here
SELECT t1.id, t1.date, t2.name, t2.age
FROM TABLE1 as t1
INNER JOIN TABLE2 as t2 ON t1.id = t2.id
TABLE JOINED
|id | date | name | age |
| 1 | 2010 | mike | 5 |
| 2 | 2010 | mike | 6 |
If you only select the date and the name, you'll get the same lines, but without the age and the id.
SELECT t1.id, t1.date, t2.name, t2.age
FROM TABLE1 as t1
INNER JOIN TABLE2 as t2 ON t1.id = t2.id
TABLE JOINED / FILTERED
| date | name |
| 2010 | mike |
| 2010 | mike |
You can see the repeated rows.
The distinct statement is for that.
The w3schools tutorial can be a little hard to follow (it's not organized in clear steps). I would recommend googling for a different one.
=========
EDIT: You changed the question.
For that you need to use GROP BY and MAX
(http://www.w3schools.com/sql/sql_groupby.asp)
(http://www.w3schools.com/sql/sql_func_max.asp)

How do I sort data from a mysql db according to a unique and predetermined order, NOT asc or desc

I need to show 1000 test questions to a student, 10 per page.
The questions are in a mysql table, the answers will go in another table.
I need each students questions to appear in a different predetermined order than any other students. The sort order is predetermined when they register and placed in a usermeta table.
In the usermeta table there is a column that lists the order in which the questions should be shown. The order in that column is unique to each student and looks like this example: 8|14|97|54|21|37|54|61 ...etc.
The first question to be shown to the student would be question #8, and then question #14, and then question #97, and so on, listing 10 per page.
I don't need to sort the questions asc or desc. Also, I can change the db structure if that would help find a solution.
Also, I can change the db structure if that would help find a
solution.
If changing the db structure is possible, then instead of storing the sorting order as a pipe separated string, store it in a separate table that maps each question to the order it should appear in for a given student. i.e.
student_id, sort_order, question_id
1 1 8
1 2 2
1 3 97
Then join on your sorting table when selecting your questions for a particular student.
SELECT q.* FROM
questions q
JOIN questions_sorting_order qso
ON q.id = qso.question_id
ORDER BY qso.sort_order
WHERE qso.student_id = :student_id
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
SELECT i2.i*10+i1.i x
, FIND_IN_SET(i2.i*10+i1.i,'8,14,97,54,21,37,54,61') y
FROM ints i1
, ints i2
HAVING y > 0
ORDER
BY y;
+----+---+
| x | y |
+----+---+
| 8 | 1 |
| 14 | 2 |
| 97 | 3 |
| 54 | 4 |
| 21 | 5 |
| 37 | 6 |
| 61 | 8 |
+----+---+
Note that 54 is ignored second time around

Transposing Rows to Columns

I am trying to come up with a single result set from two tables in a way that I have never done, and I am having a little bit of trouble figuring out how to do it or even what to search for in these forums. Consider the following hypothetical table data:
Table1
----------------------------------
ID | Name
----------------------------------
1 | aa
2 | bb
3 | cc
4 | dd
5 | ee
Table2
----------------------------------
ID | Table1_ID | Value
----------------------------------
1 | 1 | good
2 | 2 | Dumb
3 | 3 | Fat
4 | 4 | Wet
5 | 5 | High
6 | 1 | Thin
7 | 2 | Tall
8 | 3 | Goofy
9 | 4 | Rich
10 | 5 | Funny
I am looking for a query or method that allows me to end up with the following result set:
Code:
aa | bb | cc | dd | ee
---------------------------------------------------------------
good | Dumb | Fat | Wet | High
Thin | Tall | Goofy | Rich | Funny
Essentially, I want the ability to take the list of names from Table1, transpose them into column headers, then put all of Table2's values into their respective columns with the ability to sort on any column. Is this possible?
Of course this can be done in SQL. But it is tricky. As the data is written in the question, you can group by t2.id in groups of 5. After that, the query is just conditional aggregation.
select max(case when t2.table1_Id = 1 then value end) as aa,
max(case when t2.table1_Id = 2 then value end) as bb,
max(case when t2.table1_Id = 3 then value end) as cc,
max(case when t2.table1_Id = 4 then value end) as dd,
max(case when t2.table1_Id = 5 then value end) as ee
from table2 t2
group by cast(t2.id - 1 / 5 as int);
Having values be implicitly related by their ids seems like a really, really bad database design. There should be some sort of entity id that combines them.
You've got two problems here:
1) Using values as column names can't be done in a clean way
2) You want to split table2.value in 2 rows: Which of the values should be on which row? Gordon Linoff uses the table2.id field for this, but if it's auto increment and your data gets some adds/deletes later on that rhythm will get broken.
There's been similar questions before. This one has an answer that gets pretty close:
mysql select dynamic row values as column names, another column as value
Here they generate the string for the query and make a prepared statement out of it.

Displaying weekly sales by day per branch mysql

I have 2 tables.
Table 1 = branch
containing
branch_id
branch_name
Table 2 = sales_data
containing
sales_id
sales_date
sales_branch
sales_profit
I need away of showing the total daily sales and total daily profit for each branch. I know how to bring back the result for a given week etc I am just really struggling on how to pull back the data.
I also need all the branches to be shown at all time and if they haven't sold anything to display a 0.
I have put together a quick image (http://i37.photobucket.com/albums/e71/dannyflap/screen_shot.jpg) of how I would like the finished. This is really driving me nuts :(
UPDATE
select sales_branch, WEEKDAY(sales_date), COUNT(sales_profit), SUM(sales_profit)
FROM sales_date
GROUP BY sales_branch, WEEKDAY(sales_date)
This then brings back the following example. Figures are made up.
sales_branch, day, units, profit:
| branch1 | 0 (as day) | 16 | 439 |
| branch1 | 1 (as day) | 12 | 651 |
| branch1 | 2 (as day) | 22 | 312 |
| branch1 | 3 (as day) | 61 | 614 |
| branch1 | 4 (as day) | 12 | 541 |
| branch1 | 5 (as day) | 24 | 102 |
| branch1 | 6 (as day) | 21 | 145 |
I guess you would have to do an outer join on table 1's branch_id and table 2's sales_branch and loop through the result to produce the table. With an outer join those branches who haven't sold anything will get a NULL, I think.
Is it the table generating you're having problem with? I don't think it's wise to do some spectacular SQL for this problem. My two cents.
You're going to need a foreign key in sales_data that maps to branch.
assuming sales_branch is something different than branch_name or branch_id, sales_data should contain:
sales_id sales_date sales_branch branch_id sales_profit
Also - you should post your code for getting back the result for a given week and we can help you build on that rather than give you the entire answer.
EDIT
Ok with modifying your sales_data table to have a foreign key branch_id to the branch table, this query should get you closer to what you're looking for.
SELECT b.branch_name, WEEKDAY(s.sales_date), COUNT(s.sales_profit), SUM(s.sales_profit)
FROM branch b
LEFT OUTER JOIN sales_data on s.branch_id = b.branch_id
GROUP BY b.branch_id, WEEKDAY(s.sales_date)

Categories