I'm currently working on a simple employee scheduling tool and have a problem with a SQL query. To explain my problem let use this two very abstract tables.
The first table simply consists of the employees
employees
======================
empId | name | ...
----------------------
10 | Scott |
11 | Schrute |
12 | Halpert |
13 | Howard |
In the second table you find the assigned tasks to each employee by day.
tasks
==============================================
tasId | name | task | date | ...
----------------------------------------------
10 | Scott | Support | 2014-02-17 |
11 | Scott | Bugfix | 2014-02-18 |
12 | Halpert | Bugfix | 2014-02-17 |
13 | Halpert | Develop | 2014-02-18 |
14 | Howard | Support | 2014-02-17 |
Now I want to know what the employees are working on on Feb 17th or if they have no tasks planned for that day. I use the following SQL query to do that.
SELECT e.name, t.task
FROM employees e LEFT JOIN tasks t ON e.name = t.name
WHERE date IS NULL OR date = DATE('2014-02-17')
The result delivers exactly what I need:
name | task
--------------------
Scott | Support
Schrute | NULL
Halpert | Bugfix
Howard | Support
And now to my problem. If I want to see the tasks of Feb 18th I get this result set:
name | task
--------------------
Scott | Bugfix
Schrute | NULL
Halpert | Develop
The reason to this is obvious, the date of Howard's tasks are neither NULL nor do they equal 2014-02-18.. What would be the best way to get the desired result?
I use MySQL and PHP.
(Sorry for the stupid title, I couldn't think of anything better..)
Move your filter to the join predicate:
SELECT e.name, t.task
FROM employees e
LEFT JOIN tasks t ON e.name = t.name AND t.date = DATE('2014-02-18');
Your query as it is will only return people who have no tasks at all, or have tasks on the date specified. It will omit people who have tasks, but not on the date specified. Consider the results of joining your sample data with no where clause:
empId | name | task | date | ...
----------------------------------------|
10 | Scott | Support | 2014-02-17 |
10 | Scott | Bugfix | 2014-02-18 |
11 | Schrute | NULL | NULL |
12 | Halpert | Bugfix | 2014-02-17 |
12 | Halpert | Develop | 2014-02-18 |
13 | Howard | Support | 2014-02-17 |
As you can see there is no record for Howard where Date = 2014-02-18, or where Date is null, this is why no record is returned for Howard. When you add the filter to the join predicate your results become:
empId | name | task | date | ...
----------------------------------------|
10 | Scott | Bugfix | 2014-02-18 |
11 | Schrute | NULL | NULL |
12 | Halpert | Develop | 2014-02-18 |
13 | Howard | NULL | NULL |
Which I think is the desired results.
You can use
UNIX_TIMESTAMP(`date`) = 0
Looks like you have some data which are stored as '0000-00-00' and its not null so adding the extra OR condition as above will return those data as well.
Related
I have a MySQL query to delete 'near' duplicate rows from a table, and while using test data outside of my project, the query appears to work as intended. When I use the same query with PHP in the project, I get an SQL error. I've been trying all sorts of different combinations of quotes and backticks and I can't seem to get this working.
Any idea what is going on here?
Problem being solved:
This table sometimes will have rows that are nearly identical, with the only exception being the as_of_date column and the total. Only the the most recent date is important, and any older data is no longer needed in this table once newer data comes in.
Table structure with example data:
+----+---------+------+-------------+-------+
| id | account | year | as_of_date | total |
+----+---------+------+-------------+-------+
| 1 | 123 | 2017 | 2017-02-02 | 250 |
| 2 | 123 | 2017 | 2017-11-24 | 790 |
| 3 | 123 | 2018 | 2018-01-30 | 55 |
| 4 | 456 | 2016 | 2016-04-04 | 500 |
| 5 | 456 | 2016 | 2016-10-10 | 300 |
| 6 | 456 | 2017 | 2017-03-12 | 44 |
| 7 | 789 | 2015 | 2015-12-23 | 2000 |
+----+---------+------+-------------+-------+
Expected Outcome:
The desired result is to delete all 'near-duplicate' rows in the table except for the most recent one (as_of_date). So there should only be at most 1 row for any given account and year. The table should look like this after the query is executed:
+----+---------+------+-------------+-------+
| id | account | year | as_of_date | total |
+----+---------+------+-------------+-------+
| 2 | 123 | 2017 | 2017-11-24 | 790 |
| 3 | 123 | 2018 | 2018-01-30 | 55 |
| 5 | 456 | 2016 | 2016-10-10 | 300 |
| 6 | 456 | 2017 | 2017-03-12 | 44 |
| 7 | 789 | 2015 | 2015-12-23 | 2000 |
+----+---------+------+-------------+-------+
The query:
$query = "DELETE FROM `my_table` AS t
WHERE t.as_of_date NOT IN (
SELECT MAX(as_of_date)
FROM (SELECT * FROM `my_table`) AS t2
WHERE t2.account = t.account AND t2.year = t.year
GROUP BY account, `year`
)";
Here is the SQL error:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'AS t
WHERE t.as_of_date NOT IN (
S' at line 1
Don't use table aliases in DELETE FROM. That is, try DELETE FROM my_table WHERE..., omitting AS t.
By the way, the only times you really need backticks are when you have table names that are the same as reserved words or have spaces in them.
SELECT * FROM `SELECT`
or
SELECT * FROM `My Favorite Table`
Wise programmers avoid those situations.
FYI, I am using PHP and MySQL.
I am working on converting our spreadsheet staff rota to a more interactive online rota system.
I can get all the functions of the rota system fine, but when it comes to formatting it to make it clear of the departments and roles within each department, it is not so obvious.
I dont want to have to create a call to the database for each department and each role within the departments, but am stumped as to how to it to group the results and then output the results correctly and with the formatting I would like.
I do understand that this may be a very vague explanation of what I am trying to get to happen, and feel free to ask as many questions as you like.
I hope I make sense :-)
Have a great day
MySQL Data
ID | Name | Dept | Role
---|--------|------|------
1 | John | 1 | 2
2 | Steve | 1 | 2
3 | Colin | 1 | 3
4 | Trevor | 1 | 3
5 | Nigel | 2 | 4
6 | Gary | 2 | 5
Desire page/table format
STAFF | ..
-------|---
John | ..
Steve | ..
-------|---
Colin | ..
Trevor | ..
-------|---
TOT D1 | ..
-------|---
Nigel | ..
-------|---
Gary | ..
-------|---
TOT D2 | ..
If you want to get the values for each employee and the sum by department you can use the WITH ROLLUP modifier of the GROUP BY clause with adds extra rows with the totals by each value of the grouped columns.
Supposing that you want to show the salary:
select coalesce(name,concat('Total D',Dept)) as Name, sum(Salary) as Salary
from Staff
group by Dept, name with rollup
having coalesce(Dept,0)!=0
Results:
| Name | Salary |
|----------|--------|
| Colin | 80000 |
| John | 100000 |
| Steve | 120000 |
| Trevor | 40000 |
| Total D1 | 340000 |
| Gary | 60000 |
| Nigel | 50000 |
| Total D2 | 110000 |
The HAVING condition if for excluding the final row with the grand total, if you want that row you have to change a bit the COALESCE() to get the proper description:
select coalesce(name,concat('Total D',Dept),'Grand total') as Name, sum(Salary) as Salary
from Staff
group by Dept, name with rollup
Results:
| Name | Salary |
|-------------|--------|
| Colin | 80000 |
| John | 100000 |
| Steve | 120000 |
| Trevor | 40000 |
| Total D1 | 340000 |
| Gary | 60000 |
| Nigel | 50000 |
| Total D2 | 110000 |
| Grand total | 450000 |
A lot of thought process went before I decided to post this question. Trying to explain my problem in a simplified format.
I have 2 tables in my mySQL table, one of which is the users and the other one is the questions and answers related to that user.
Simplified Example:
Users
| id | name | registered_on |
|----|--------|---------------------|
| 1 | Aaron | 2017-02-01 00:01:02 |
| 2 | Baron | 2017-02-01 01:01:02 |
| 3 | Chiron | 2017-02-01 02:01:02 |
Answer Keys
| id | user_id | keyword | value | created_on |
|----|---------|---------|---------|---------------------|
| 1 | 1 | gender | Male | 2017-02-01 00:01:02 |
| 2 | 1 | age | 24 | 2017-02-01 00:01:02 |
| 3 | 2 | gender | Male | 2017-02-01 00:01:02 |
| 4 | 2 | age | Unknown | 2017-02-01 00:01:02 |
| 5 | 3 | gender | God | 2017-02-01 00:01:02 |
I hope the relation above is clear. So what I wish to achieve is to create a CSV report like this
| name | gender | age | registered_on |
|--------|--------|---------|---------------------|
| Aaron | Male | 24 | 2017-02-01 00:01:02 |
| Baron | Male | Unknown | 2017-02-01 01:01:02 |
| Chiron | God | NULL | 2017-02-01 02:01:02 |
As my research suggests, this can be done in the following ways :
Prepared Statements (Cannot use because CodeIgniter does not Support)
Paging (The vertical table is the problem)
MySQL Pivot Tables (But with Dynamic column names -- feels complicated!)
Any other better way that I do not know of
I am thinking about paging but am yet to figure out how it could be
used in the case of vertical tables. I would like it if any of you guys have faced the same problem or have some meaningful suggestions! Any help is appreciated!
If I understand well your problem, you want a CSV file with all user informations available using the answer keys as columns?
Why not doing this with 2 queries?
First, you can select all different keys in your answer table (select distinct keyword...)
Then, create your query with joins looping on your results :
$this->db->select('u.id, u.name, u.registered_on');
$this->db->from('users u');
foreach($keywords as $i_key => $s_keyword){
$this->db->join('answers_table at'.$i_key, 'at'.$i_key.'.user_id = u.id', 'left');
$this->db->select('at'.$i_key.'.value as '.$s_keyword);
}
$a_users = $this->db->get()->result();
I am new for PHP and MYSQL. In my study I met a problem
I have a table like this:
+----+----------+---------+
| time | name | number |
+----+----------+---------+
| 12.1 | google | 10 |
| 12.2 | yahoo | 15 |
| 12.3 | msn | 20 |
| 12.1 | google | 10 |
| 12.1 | google | 29 |
| 12.2 | yahoo | 10 |
+----+----------+---------+
but I want the talbe like this:
+----+----------+---------+
| time | name | number |
+----+----------+---------+
| 12.2 | yahoo | 15 |
| 12.3 | msn | 20 |
| 12.1 | google | 29 |
+----+----------+---------+
when the time and the name are the same, I want the row with the max number,
what should I do? I am very worrying about this problem and thank you for anwsing me
TRY ( not tested )
SELECT `time`, `name`, `number`
FROM tbl
GROUP BY `name`,`time`
HAVING MAX(`number`)
tip : TIME is a mysql reserve keyword so wrap it with ` and create Index on name and time column together
Try it.
Select time, name, number from [table name] group by time, name having max(number)
Hopefully this is a quick and easy question to answer.
I have a MySQL Database with the following fields:
shoe_size and waist_size
The shoe_size entries range anywhere from 3 to 21 (incrementing by halves-- 3, 3.5, 4, 4.5, etc).
The same is true for the waist_size entries.
My question is this:
Using PHP, how can I best consolidate this into a table that counts the total number of sizes, so that it could essentially be used as an Order Form?
For example, the MySQL table looks like this:
----------------------------------------------
| Name | shoe_size | waist_size |
----------------------------------------------
| John | 9.5 | 33 |
----------------------------------------------
| Steve | 9 | 32 |
----------------------------------------------
| Tom | 9.5 | 33 |
----------------------------------------------
| Sally | 7 | 8 |
----------------------------------------------
| Jane | 7 | 8 |
----------------------------------------------
And the output HTML (table?) would look something like this:
ORDER FORM
------------------------------------------
| Shoe Size | 7 | 9 | 9.5 |
------------------------------------------
| Total Pairs | 2 | 1 | 2 | <----- This is calculated from above
------------------------------------------
------------------------------------------
| Waist Size | 8 | 32 | 33 |
------------------------------------------
| Total Pairs | 2 | 1 | 2 | <----- This is calculated from above
------------------------------------------
I'm not even sure if my format for the "Order Form" table is the best way to go on this. Any nudge in the right direction would be greatly appreciated.
MySQL might be enough for this task, try something like
SELECT shoe_size, COUNT(id) as count FROM table GROUP BY shoe_size ORDER BY shoe_size asc
I think what you are looking for is GROUP BY.
(http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html)
e.g:
SELECT `Shoe Size`, COUNT(*) FROM table_name GROUP BY `Shoe Size`