strange behavior of query - php

I have the table below:
+-------------+------------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+-------------------+----------------+
| category_id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| title | varchar(64) | NO | | NULL | |
| description | text | NO | | NULL | |
| created_on | timestamp | NO | | CURRENT_TIMESTAMP | |
| updated_on | timestamp | YES | | NULL | |
+-------------+------------------+------+-----+-------------------+----------------+
I needed to delete some records according to the date created and executed a set of queries:
mysql> select created_on from categories limit 10;
+---------------------+
| created_on |
+---------------------+
| 2013-01-14 09:26:21 |
| 2012-08-15 11:18:38 |
| 2012-09-06 06:44:46 |
| 2012-09-06 23:27:14 |
then
mysql> select date('2013-01-14 09:26:21');
+-----------------------------+
| date('2013-01-14 09:26:21') |
+-----------------------------+
| 2013-01-14 |
+-----------------------------+
and finally I got:
mysql> select DATE('created_on') from categories limit 10;
+--------------------+
| DATE('created_on') |
+--------------------+
| NULL |
| NULL |
| NULL |
| NULL |
As you can see the date wasn't converted correctly. In about 10 minutes is worked ok:
mysql> select date(created_on) from categories limit 10;
+------------------+
| date(created_on) |
+------------------+
| 2013-01-14 |
| 2012-08-15 |
| 2012-09-06 |
| 2012-09-06 |
I'm sure that the data were intact while I was running the queries.
So my question:
Can anybody explain why date function showed different results on the same input?

You have to use::
select DATE(created_on) from categories limit 10;
By using the inverted comma, you are asking the database to treat 'created_on' as parameter, and since it cannot be converted to date type, you are getting null

select DATE('created_on') from categories limit 10;
String versus column:
select DATE(created_on) from categories limit 10;

Related

Doing a proper Join with SQL

Suppose I have two tables, one with list of spells and another with a grimory, the list of spells that a user has selected for learn or already learned.
mysql> SHOW COLUMNS FROM Grimory;
+--------------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| personage_id | int(11) | YES | MUL | NULL | |
| spell_id | int(11) | YES | MUL | NULL | |
| isLearned | tinyint(1) | NO | | NULL | |
| isSelected | tinyint(1) | NO | | NULL | |
+--------------+------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
mysql> SHOW COLUMNS FROM Spell;
+-----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| description | longtext | NO | | NULL | |
| chatDescription | longtext | NO | | NULL | |
| level | int(11) | NO | | NULL | |
| isActive | tinyint(1) | NO | | NULL | |
| category_id | int(11) | YES | MUL | NULL | |
| createdAt | datetime | YES | | NULL | |
+-----------------+--------------+------+-----+---------+----------------+
8 rows in set (0.00 sec)
The problem.
I want to display a list with ALL spells by category_id, BUT for every row I want to show if that spell is learned or selected by current user (personage_id).
Can you help me to write a proper query?
How this works:
With php I generate: List of spells | checkbox isSelected | checkBox isStudied
When I click on isSelected checkbox, a record in grimory will be added with current user and spell.
Updated
SELECT a.*, IFNULL(b.isLearned,0) as isLearned,
IFNULL(b.isSelected,0) as isSelected
FROM Spell a
LEFT JOIN Grimory b(ON b.spell_id =a.id
AND b.personage_id =:current_user_id)
WHERE a.category_id = :current_category_id
SELECT * FROM Spell
INNER JOIN Grimory
ON Spell.id = Grimory.spell_id
WHERE (Grimory.isLearned = 1 OR Grimory.isSelected = 1)
AND Spell.category_id = 'YOUR CAT ID IS HERE'

Number of games a particular user has played today where there is no data

I'm trying to get the number of games that summoners in the queue have played today. I have tried several queries. The closest I have gotten is:
SELECT
s.summoner,
s.wins,
COUNT(*) as playedToday
FROM summoners s
LEFT JOIN teams_players tp ON tp.summoner_id = s.summoner
LEFT JOIN teams t ON t.id = tp.team_id
LEFT JOIN matches b ON t.id = b.blue_id
WHERE
b.played_on = DATE(NOW())
s.in_queue;
But that doesn't give the count per-user. It just gets the overall count.
None of the tables have any data on any of the summoners. What I'd like is something like:
+-----------------+------+-------------+
| summoner | wins | gamesPlayed |
+-----------------+------+-------------+
| DotAliscious | 353 | 0 |
| Kraator | 440 | 0 |
| Nammon | 667 | 0 |
| VictorousSecret | 843 | 0 |
| Canas | 544 | 0 |
| Sprognak | 502 | 0 |
| Ghostilocks | 808 | 0 |
| b0b d0e | 224 | 0 |
| Metroshica | 339 | 0 |
| RubenatorX | 478 | 0 |
+-----------------+------+-------------+
I can't think how to express this problem generically, which is why I haven't found a solution.
My data set. Note that teams.id matches up with matches.blue_id and matches.purple_id.
mysql> DESCRIBE summoners;
+--------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| icon | int(11) | NO | | 0 | |
| summoner | varchar(32) | NO | UNI | NULL | |
| skype | varchar(32) | NO | | NULL | |
| email | varchar(32) | NO | | NULL | |
| in_queue | tinyint(1) | NO | | 0 | |
| wins | int(11) | NO | | 0 | |
| level | int(11) | NO | | 1 | |
| lan_wins | int(11) | NO | | 0 | |
| played_today | int(11) | NO | | 0 | |
+--------------+-------------+------+-----+---------+----------------+
10 rows in set (0.00 sec)
mysql> DESCRIBE matches;
+-----------+-------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| blue_id | int(11) | NO | MUL | NULL | |
| purple_id | int(11) | NO | MUL | NULL | |
| status | varchar(32) | NO | | display | |
| played_on | datetime | NO | | 2012-04-06 13:53:55 | |
+-----------+-------------+------+-----+---------------------+----------------+
5 rows in set (0.00 sec)
mysql> DESCRIBE teams;
+---------+-------------+------+-----+------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| outcome | varchar(32) | NO | | incomplete | |
+---------+-------------+------+-----+------------+----------------+
2 rows in set (0.00 sec)
mysql> DESCRIBE teams_players;
+-------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| team_id | int(11) | NO | MUL | NULL | |
| summoner_id | int(11) | NO | MUL | NULL | |
+-------------+---------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
You're going to want to use a subselect to get the count of records that meet the condition, and that will return you a 0 if the condition is false. Something along the lines of
(SELECT COUNT(*) FROM matches b WHERE b.blue_id = t.id AND b.played_on = DATE(NOW())) AS playedToday
and you will want to remove the LEFT JOIN to the matches table and the WHERE clause checking the date.
Your played_on is datetime and you are comparing it with date maybe that's your problem. YOu should try:
WHERE
DATE(b.played_on) = DATE(NOW())
Also what do you mean by s.in_queue? Did you forget the AND? Do you mean
WHERE
DATE(b.played_on) = DATE(NOW())
AND
s.in_queue;

SQL Query using MySQL sample employee database

I have a problem making an advanced (for me) query.
I am trying to get the first name, last name, title, department name, and first and last name of the manager. I am using this SQL in PHP if for some reason you needed to know. I limit to 10 because the database is enormous. I need only the entry for the employees latest data. They have more than one entry because of job promotions, etc.
I included everything I think you would need to understand my problem. Thanks if you can help.
mysql>
SELECT DISTINCT employees.first_name, employees.last_name,
titles.title, departments.dept_name, z.first, z.last
FROM employees, dept_emp, departments, titles,
(
SELECT employees.first_name AS first, employees.last_name AS last
FROM employees, dept_emp, dept_manager
WHERE
employees.emp_no = dept_emp.emp_no
AND dept_manager.emp_no = dept_emp.emp_no
) AS z
WHERE
employees.emp_no = dept_emp.emp_no
AND dept_emp.dept_no = departments.dept_no
AND titles.emp_no = employees.emp_no
LIMIT 10;
+------------+-----------+-----------------+-------------+-----------+--------------+
| first_name | last_name | title | dept_name | first | last |
+------------+-----------+-----------------+-------------+-----------+--------------+
| Georgi | Facello | Senior Engineer | Development | Margareta | Markovitch |
| Georgi | Facello | Senior Engineer | Development | Vishwani | Minakawa |
| Georgi | Facello | Senior Engineer | Development | Ebru | Alpin |
| Georgi | Facello | Senior Engineer | Development | Isamu | Legleitner |
| Georgi | Facello | Senior Engineer | Development | Shirish | Ossenbruggen |
| Georgi | Facello | Senior Engineer | Development | Karsten | Sigstam |
| Georgi | Facello | Senior Engineer | Development | Krassimir | Wegerle |
| Georgi | Facello | Senior Engineer | Development | Rosine | Cools |
| Georgi | Facello | Senior Engineer | Development | Shem | Kieras |
| Georgi | Facello | Senior Engineer | Development | Oscar | Ghazalie |
+------------+-----------+-----------------+-------------+-----------+--------------+
10 rows in set (0.00 sec)
The database is the employees database from mysql.com:
http://dev.mysql.com/doc/index-other.html
mysql> desc departments;
+-----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| dept_no | char(4) | NO | PRI | NULL | |
| dept_name | varchar(40) | NO | UNI | NULL | |
+-----------+-------------+------+-----+---------+-------+
2 rows in set (0.22 sec)
mysql> desc dept_emp;
+-----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| dept_no | char(4) | NO | PRI | NULL | |
| from_date | date | NO | | NULL | |
| to_date | date | NO | | NULL | |
+-----------+---------+------+-----+---------+-------+
4 rows in set (0.20 sec)
mysql> desc dept_manager;
+-----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| dept_no | char(4) | NO | PRI | NULL | |
| emp_no | int(11) | NO | PRI | NULL | |
| from_date | date | NO | | NULL | |
| to_date | date | NO | | NULL | |
+-----------+---------+------+-----+---------+-------+
4 rows in set (0.21 sec)
mysql> desc employees;
+------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| birth_date | date | NO | | NULL | |
| first_name | varchar(14) | NO | | NULL | |
| last_name | varchar(16) | NO | | NULL | |
| gender | enum('M','F') | NO | | NULL | |
| hire_date | date | NO | | NULL | |
+------------+---------------+------+-----+---------+-------+
6 rows in set (0.32 sec)
mysql> desc salaries;
+-----------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| salary | int(11) | NO | | NULL | |
| from_date | date | NO | PRI | NULL | |
| to_date | date | NO | | NULL | |
+-----------+---------+------+-----+---------+-------+
4 rows in set (0.34 sec)
mysql> desc titles;
+-----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| title | varchar(50) | NO | PRI | NULL | |
| from_date | date | NO | PRI | NULL | |
| to_date | date | YES | | NULL | |
+-----------+-------------+------+-----+---------+-------+
4 rows in set (0.60 sec)
I am trying to get the first name, last name, title, department name, and first and last name of the manager
Using the theory (but it's probably not optimized if you have large tables), do it step by step. Assuming you have a $date PHP variable that contains a date (like the result from date('Y-m-d 00:00:00')), or otherwise you may use MySQL's date.
1/ get employees' identity:
SELECT
e.first_name,
e.last_name
FROM
employees AS e
WHERE
1
2/ add his/her current title
SELECT
e.first_name,
e.last_name,
t.title
FROM
employees AS e,
titles AS t
WHERE
e.emp_no=t.emp_no AND t.from_date<='$date' AND t.to_date>='$date'
3/ get his/her current department (from dept_emp) and retrieve the department's name
SELECT
e.first_name,
e.last_name, t.title,
d.dept_name
FROM
employees AS e,
titles AS t,
dept_emp AS de,
departments as d
WHERE
e.emp_no=t.emp_no AND t.from_date<='$date' AND t.to_date>='$date'
AND
e.emp_no=de.emp_no AND de.from_date<='$date' AND de.to_date>='$date'
AND
d.dept_no=de.dept_no
4/ get the department's manager (from dept_manager) and retrieve his/her name (from employees)
SELECT
e.first_name AS empFN,
e.last_name AS empLN,
t.title AS empT,
d.dept_name AS dept,
em.first_name AS manFN,
em.last_name AS manLN
FROM
employees AS e,
titles AS t,
dept_emp AS de,
departments as d,
dept_manager AS dm,
employees AS em
WHERE
e.emp_no=t.emp_no AND t.from_date<='$date' AND t.to_date>='$date'
AND
e.emp_no=de.emp_no AND de.from_date<='$date' AND de.to_date>='$date'
AND
d.dept_no=de.dept_no
AND
em.emp_no=dm.emp_no AND dm.from_date<='$date' AND dm.to_date>='$date'
AND
de.dept_no=dm.dept_no
There you can append a LIMIT (and an ORDER BY probably, you don't know which results will be picked in case of a limit), and/or add in the WHERE an emp_no for the employee(s) you want to have info about. Not tested though, but it's mainly to let you understand how to build the request.
This is theory, and this request is probably be as heavy to process as the one you wrote. Since you use SQL with PHP, it may be better to retrieve only the primary keys you're interested in, then send short requests to retrieve the information you want. Something like that I mean (using fake functions but the spirit's here):
$dateString="from_date<='$date' AND to_date>='$date'";
$qe=query("SELECT emp_no, first_name, last_name, dept_no FROM employees, dept_emp WHERE dept_emp.emp_no=employees.emp_no LIMIT 10 ORDER BY emp_no DESC");
while($r=fetch($qe)) {
$qt=fetch(query("SELECT title FROM titles WHERE emp_no=".$qe['emp_no']." AND ".$dateString));
$qd=fetch(query("SELECT dept_name FROM departments WHERE dept_no=".$qe['dept_no']." AND ".$dateString));
$qm=fetch(query("SELECT first_name, last_name FROM employees AS e, dept_manager AS dm WHERE dept_no=".$qe['dept_no']." AND e.emp_no=dm.emp_no AND ".$dateString));
//echo/process here
}
Hope this helps :)

mysql multi group query with count

I have this query
SELECT
products_list.id_cat_unique,
products_categories_list.*,
COUNT(products_list.id_cat_unique) as counter
FROM products_categories_ids
LEFT JOIN products_list
ON products_categories_ids.id_cat_unique = products_list.id_cat_unique
LEFT JOIN products_categories_list
ON products_categories_list.id_cat_unique = products_categories_ids.id_cat_unique
GROUP BY products_categories_list.name_cat
ORDER BY products_categories_list.name_cat ASC
This is complicated to explain without having the tables on your screen but I'll try
what I want is count all the rows from products_list that can be associated with max two rows per id_cat_unique (contained in products_categories_list) that are English or Italian. This two items each could have infinite id_products in products_list of course.
I want the result to be grouped by products_categories_list.name_cat and by the language.
What I get with this query is categories grouped by name/unique id which is correct, the problem is that the counter value for both English and Italian turns out as the sum of all the products associated with one id_cat_unique regardless of the language. So if I have 12 rows from one category in English and 3 for the same category in Spanish I get 15 as counter for both the languages.
EDIT ADDING TABLES STRUCTURES
products_list
+-------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+--------------+------+-----+---------+----------------+
| id_product | int(11) | NO | PRI | NULL | auto_increment |
| id_product_unique | int(5) | NO | | NULL | |
| lang | varchar(2) | NO | | NULL | |
| name_product | varchar(200) | NO | | NULL | |
| desc_product | text | NO | | NULL | |
| id_cat_unique | int(2) | NO | | NULL | |
| status | int(1) | NO | | NULL | |
+-------------------+--------------+------+-----+---------+----------------+
12 rows in set (0.00 sec)
products_categories_ids
+---------------+--------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------+------+-----+---------+----------------+
| id_cat_unique | int(5) | NO | PRI | NULL | auto_increment |
+---------------+--------+------+-----+---------+----------------+
1 row in set (0.00 sec)
products_categories_list
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id_cat | int(5) | NO | PRI | NULL | auto_increment |
| id_cat_unique | int(2) | NO | | NULL | |
| lang | varchar(2) | NO | | NULL | |
| name_cat | varchar(500) | NO | | NULL | |
| date_created | int(11) | NO | | NULL | |
| date_modified | int(11) | NO | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
6 rows in set (0.01 sec)
result from query
| id_cat_unique | id_cat | id_cat_unique | lang | name_cat | date_created | date_modified | counter |
+---------------+--------+---------------+------+--------------------------------+--------------+---------------+---------+
| 1 | 18 | 1 | it | Carne di suino | 1308267538 | 1308267538 | 6 |
| 14 | 21 | 14 | it | Guanciali | 1308777322 | 1308777322 | 2 |
| 3 | 20 | 3 | it | Pollo a pezzi | 1308267892 | 1308267892 | 2 |
| 1 | 22 | 1 | en | Pork meat | 1308267538 | 1312383232 | 6 |
| 14 | 23 | 14 | en | Sheeps | 1308777322 | 1312383220 | 2 |
| 2 | 19 | 2 | it | That's a "test" | 1308267538 | 1308267538 | 7 |
+---------------+--------+---------------+------+--------------------------------+--------------+---------------+---------+
6 rows in set (0.00 sec)
"Pork meat" and "Carne di Suino" have respectively 1 and 5 products (from the same category but different language, i wanna group the counting by language too) instead it shows 6 for both
NEW question
I'd like to use this query for many purposes without using different things, so accordingly if I want to retrieve the categories with ONE language I'd add one where clause
select pcl.*,
(select count(*) from products_list pl
where pcl.id_cat_unique = pl.id_cat_unique
and pcl.lang = pl.lang) as counter
from products_categories_list pcl
where products_list.lang = 'en' <- added
ORDER BY pcl.name_cat ASC
I get Unknown column 'products_list.lang' in 'where clause'. Why?
I THINK this will work, if not, it's going to be close, let me know what you get, and I can modify it. I've modified the query around. Would this work?
select pcl.*,
(select count(*) from products_list pl
where pcl.id_cat_unique = pl.id_cat_unique
and pcl.lang = pl.lang) as counter
from products_categories_list pcl
ORDER BY pcl.name_cat ASC
The PCL and PL are just aliases for the tables, so I didn't have to always write out products_categories_list and products_list. You specify the alias in the from part of the statement, so from products_list pl allows you to refer to products_list as pl in the rest of the query.
The idea behind this is that you have a unique row for each products_categories_list and you want a count of a secondary table. This could have been done by group by or by a sub query. I put the correlated subquery in to count the number of rows in products_list that matched the products_categories_list and language, since you are getting all the columns out of the products_categories_list. The correlated subquery allows you to get a single value (count(*)) from a nested query.
Probably this: add DISTINCT
COUNT(DISTINCT products_list.id_cat_unique) as counter
This will ignore duplicate, so the 12+3 via the JOIN becomes 1+1

Force Specific Record to Top When Performing GROUP BY

I have the following MySQL query and tables from which I am querying:
SELECT
`Song`.`id`,
`Song`.`file_name`,
`User`.`first_name`,
`Vote`.`value`,
Sum(`Vote`.`value`) AS score
FROM `songs` AS `Song`
LEFT JOIN votes AS `Vote` ON (`Song`.`id`=`Vote`.`song_id`)
LEFT JOIN `users` AS `User` ON (`Song`.`user_id` = `User`.`id`)
GROUP BY `Vote`.`song_id`
LIMIT 20;
mysql> describe songs;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| file_name | varchar(255) | NO | | NULL | |
| user_id | int(11) | NO | | NULL | |
| created | datetime | NO | | NULL | |
| modified | datetime | NO | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
mysql> describe users;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(255) | NO | | NULL | |
| password | varchar(255) | NO | | NULL | |
| first_name | varchar(255) | NO | | NULL | |
| last_name | varchar(255) | NO | | NULL | |
| is_admin | tinyint(1) | NO | | 0 | |
| created | datetime | NO | | NULL | |
| modified | datetime | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
mysql> describe votes;
+----------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| value | int(11) | NO | | NULL | |
| song_id | int(11) | NO | | NULL | |
| user_id | int(11) | NO | | NULL | |
| created | datetime | NO | | NULL | |
| modified | datetime | NO | | NULL | |
+----------+----------+------+-----+---------+----------------+
This query functions just like I want except for one thing. The value returned in the field Vote.value is not from a row that is associated with the user who is logged into the application. I need the score value to be a sum of all the values no matter which user it is associated with, but the Vote.value field should belong to the logged in user (each user only gets one vote record per song).
My first thought is to somehow sort the table so that when the group by happens the vote record for the logged in user is at the top but I have no idea how to do a sort that forces an arbitrary value to the top. Any ideas would be very helpful.
and a third join
LEFT JOIN votes AS `VotePerUser` ON (`Song`.`id`=`Vote`.`song_id`
AND `Song`.`user_id`=`votes`.`user_id`)
and replace the Vote.value with VotePerUser.Value

Categories