Unknown column in 'on clause' after MySQL upgrade - mysql

I've been updating some old code which used PHP4 and MySQL 4.1 up to MySQL 5.6 / MariaDB 10. I've had a few issues with SQL JOINs
and precedence but this one has really stumped me and is giving me the error -
#1054 - Unknown column 'grouping_id' in 'on clause'
I've tried messing about with the order of the JOIN statements in the query below but I haven't had any success yet, as mentioned this query worked fine on MySQL 4.
SELECT
team.team_id,
team.team_name,
competition.rel_sport_id,
country.country_name
FROM
team
LEFT JOIN team_grouping ON(
rel_team_id = team_id AND team_grouping.rel_grouping_id = grouping_id
)
LEFT JOIN grouping ON grouping_id = team_grouping.rel_grouping_id
LEFT JOIN country ON team.rel_country_id = country_id
LEFT JOIN sport ON team.rel_sport_id = sport_id
LEFT JOIN competition_country ON(
rel_competition_id = competition_id AND competition_country.rel_country_id = country_id
)
LEFT JOIN competition ON competition_id = '985'
WHERE
team.rel_country_id = competition_country.rel_country_id AND team.rel_sport_id = competition.rel_sport_id AND grouping_id = '3'
ORDER BY
team_name
Can anyone help with what could be wrong with the above query?
EDIT - Added table schemas:
CREATE TABLE `grouping` (
`grouping_id` int(11) NOT NULL AUTO_INCREMENT,
`grouping_name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`grouping_id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=latin
CREATE TABLE `team` (
`team_id` int(11) NOT NULL AUTO_INCREMENT,
`team_name` varchar(200) DEFAULT NULL,
`image` varchar(100) DEFAULT NULL,
`rel_country_id` int(11) DEFAULT NULL,
`rel_sport_id` int(11) DEFAULT NULL,
`modified_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`team_id`),
KEY `IDX_team_1` (`rel_country_id`),
KEY `IDX_team_2` (`rel_sport_id`)
) ENGINE=MyISAM AUTO_INCREMENT=11918 DEFAULT CHARSET=latin1
CREATE TABLE `country` (
`country_id` int(11) NOT NULL AUTO_INCREMENT,
`country_name` varchar(40) DEFAULT NULL,
`image` varchar(40) DEFAULT NULL,
`rel_geographic_id` int(11) DEFAULT NULL,
PRIMARY KEY (`country_id`),
KEY `IDX_country_2` (`rel_geographic_id`)
) ENGINE=MyISAM AUTO_INCREMENT=237 DEFAULT CHARSET=latin1
CREATE TABLE `competition` (
`competition_id` int(11) NOT NULL AUTO_INCREMENT,
`competition_name` varchar(200) DEFAULT NULL,
`rel_sport_id` int(11) DEFAULT NULL,
`rel_grouping_id` int(11) DEFAULT NULL,
`rel_competition_tz_id` int(11) NOT NULL DEFAULT '2',
`modified_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`competition_id`),
KEY `IDX_competition_1` (`rel_sport_id`),
KEY `IDX_competition_2` (`rel_grouping_id`)
) ENGINE=MyISAM AUTO_INCREMENT=1133 DEFAULT CHARSET=latin1
CREATE TABLE `sport` (
`sport_id` int(11) NOT NULL AUTO_INCREMENT,
`sport_name` varchar(40) DEFAULT NULL,
`image` varchar(40) DEFAULT NULL,
`modified_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`sport_id`)
) ENGINE=MyISAM AUTO_INCREMENT=29 DEFAULT CHARSET=latin1
CREATE TABLE `competition_country` (
`rel_competition_id` int(11) NOT NULL DEFAULT '0',
`rel_country_id` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`rel_competition_id`,`rel_country_id`),
KEY `IDX_competition_country_1` (`rel_competition_id`),
KEY `IDX_competition_country_2` (`rel_country_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

Table names in an ON clause can only refer to tables preceding it in the query. When you're joining through a relationship table, the first ON clause just relates with the table before it, you relate to the second table in the next ON clause.
So the ON clause for team_grouping should only have rel_team_id = team_id, and the ON clause for competition_country should only have rel_country_id = country_id.
I don't think you need the WHERE clause at the end. Those relationships should be implied already by the earlier joins. And since you're doing a LEFT JOIN with grouping, you should put restrictions on that table in the ON clause; otherwise, the null values from non-matching rows will be filtered out by the WHERE clause.
SELECT
team.team_id,
team.team_name,
competition.rel_sport_id,
country.country_name
FROM team
LEFT JOIN team_grouping ON rel_team_id = team_id
LEFT JOIN grouping ON grouping_id = team_grouping.rel_grouping_id AND grouping_id = 3
LEFT JOIN country ON team.rel_country_id = country_id
LEFT JOIN sport ON team.rel_sport_id = sport_id
LEFT JOIN competition_country ON competition_country.rel_country_id = country_id
LEFT JOIN competition ON competition_id = '985' AND competition_id = competition_country.rel_competition_id
ORDER BY team_name

Related

Getting Group by with left join to work in MySQL 5.7

Im trying to get a query to only return a single row for each unique ID.
given the following:
CREATE TABLE `groups` ( `id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL, `type` int(1) DEFAULT NULL,
`date` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB
AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
CREATE TABLE `groupmembers` ( `id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL, `readmessage` int(1) DEFAULT
NULL, `status` int(1) DEFAULT NULL, `group_id` int(20) DEFAULT
NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT
CHARSET=latin1;
CREATE TABLE `messages` ( `id` bigint(10) NOT NULL AUTO_INCREMENT,
`send_to` varchar(50) NOT NULL DEFAULT '', `name` varchar(50) NOT
NULL DEFAULT '', `email` varchar(50) NOT NULL DEFAULT '',
`subject` varchar(100) NOT NULL DEFAULT 'No Subject', `message`
longtext NOT NULL, `timestamp` timestamp NOT NULL DEFAULT
CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `message_read`
enum('Yes','No') NOT NULL DEFAULT 'No', `send_time` datetime NOT
NULL DEFAULT '1970-01-01 00:00:00',
`hide_message` enum('0','1') NOT NULL, `grouper` int(20) DEFAULT
NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT
CHARSET=latin1;
insert into groups (type,date) values ('0',now());
insert into groupmembers (username,group_id) values ('user1',5),('user2',5);
insert into messages (send_to,name,send_time,grouper,message) values
('user1','user2','2019-10-09 19:18:12',5,'hello');
insert into messages (send_to,name,send_time,grouper,message) values
('','user2','2019-10-10 09:18:39',5,'hello');
In mysql 5.6 this query works as anticipated:
select groups.id,send_to,messages.name,max(send_time) from groups
inner join messages on groups.id=messages.grouper left join
groupmembers on groups.id=groupmembers.group_id where
groupmembers.username='user1' group by groups.id ORDER BY
MAX(send_time)
in mysql 5.7 this returns an error
Error Code: 1055. Expression #2 of SELECT list is not in GROUP BY
clause and contains nonaggregated column 'messages.send_to' which is
not functionally dependent on columns in GROUP BY clause; this is
incompatible with sql_mode=only_full_group_by
What do I need to do to get this to work in MySQL 5.7 without adjusting the SQL Mode?
See SQL Fiddle here: http://sqlfiddle.com/#!9/9061da/4
It works as expected on the SQL fiddle as that is running 5.6.
Just a guess...
SELECT DISTINCT g.id
, m.send_to
, m.name
, m.send_time
FROM groups g
JOIN messages m
ON m.grouper = g.id
JOIN
( SELECT send_to
, name
, MAX(send_time) send_time
FROM messages m
GROUP
BY send_to
, name
) x
ON x.send_to = m.send_to
AND x.name = m.name
AND x.send_time = m.send_time
JOIN groupmembers gm
ON gm.group_id = g.id
WHERE gm.username = 'user1'
ORDER
BY send_time;

mysql gives NULL for record in a table if JOIN and COUNT used but SELECT works fine why?

Table 1
CREATE TABLE IF NOT EXISTS `com_msg` (
`msg_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`msg_to` int(10) NOT NULL,
`msg_from` int(10) NOT NULL,
`msg_new` tinyint(1) unsigned NOT NULL DEFAULT '1',
`msg_content` varchar(300) NOT NULL,
`msg_date` date NOT NULL,
`bl_sender` tinyint(1) unsigned NOT NULL DEFAULT '0',
`bl_recip` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`msg_id`),
UNIQUE KEY `msg_id` (`msg_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Table 2
CREATE TABLE IF NOT EXISTS `ac_vars` (
`user_id` int(10) unsigned NOT NULL,
`ac_ballance` smallint(3) unsigned NOT NULL DEFAULT '0',
`prof_views` mediumint(8) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`user_id`),
UNIQUE KEY `id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
When I use query :
SELECT ac_ballance, prof_views, COUNT( msg_id ) AS messages
FROM ac_vars
INNER JOIN com_msg ON user_id = msg_to
WHERE user_id =".$userid." AND com_msg.msg_new =1;
I get :
ac_ballance=NULL(incorrect)
prof_views=NULL(incorrect)
messages=0(correct)
But with Select statement just on ac_vars I get correct values, what is the correct way of doing this?
You want rows from the table ac_vars even when there's no corresponding row in the table com_msg.
So you must use a LEFT JOIN:
SELECT ac_ballance, prof_views, COUNT( msg_id ) AS messages
FROM ac_vars
LEFT JOIN com_msg
ON user_id = msg_to AND com_msg.msg_new =1
WHERE user_id =".$userid.";
Please note that the condition
com_msg.msg_new =1
got to be a part of the JOIN condition and not the WHERE clause, because there's no value in com_msg that fulfills this condition.
Note
Adding
GROUP BY ac_ballance, prof_views
is not needed by MySQLs optimization because the values in those columns are directly dependent of the user_id and the WHERE clause permits only one single row.

Select the employee who earned more

[MySQL] I want to select the employee who earned more, the problem is that I am using two tables.
Finally I have no idea how to do it.
I built a query that can count the number of services and sort by most, but the query is flawed as there services with higher values​​.
here is the table structure.
CREATE TABLE IF NOT EXISTS `contas` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_servico` int(11) DEFAULT NULL,
`id_funcionario` int(11) DEFAULT NULL,
`data` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=28 ;
CREATE TABLE IF NOT EXISTS `servicos` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`titulo` varchar(250) NOT NULL COMMENT 'Título do serviço',
`descri` mediumtext COMMENT 'uma pequena descrição do serviço',
`valor` varchar(10) NOT NULL COMMENT 'valor bruto do serviço',
`comissao` varchar(10) DEFAULT NULL COMMENT 'comissão por funcionario',
`data` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;
Select all employees, join their "services", then group by employee ID, sum their income and select the first one.
SELECT E.id, SUM(S.amount) AS income
FROM employee E
INNER JOIN service S ON S.employee_id = E.id
GROUP BY E.id
ORDER BY income DESC
LIMIT 1

Join two tables only if a value is null or a specific number

I have three tables in a database:
Product table - +100000 entries
Attribute table (list of possible attributes of a product)
Product attribtue table (which contains the value of the attribute of a product)
I am looking for 8 random products and one of their attributes (attribute_id = 2), but if a product hasn't this attribute it should appear at the return of the query. I have been trying some sql queries without any succesful result because my return only shows the products that have the attribute and hide the others.
My three tables are like this:
CREATE TABLE `product` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sku` varchar(20) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`name` varchar(90) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`provider_id` int(11) unsigned DEFAULT NULL,
`url` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`active` int(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `sku_UNIQUE` (`sku`)
) ENGINE=InnoDB AUTO_INCREMENT=123965 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `attribute` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL DEFAULT '',
`data_type` varchar(50) DEFAULT '',
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=latin1;
CREATE TABLE `product_attribute` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`product_id` int(11) unsigned NOT NULL,
`attribute_id` int(11) unsigned NOT NULL DEFAULT '6',
`value` longtext NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `product_id` (`product_id`,`attribute_id`)
) ENGINE=InnoDB AUTO_INCREMENT=869437 DEFAULT CHARSET=latin1;
And this is one of the queries I tried, I thought it was correct but it have the same problem as the others...
SELECT product.id, product.sku, product.name,provider.name as provider_name,
product_attribute.value as author
FROM (`product`)
LEFT JOIN `provider` ON `product`.`provider_id` = `provider`.`id`
LEFT JOIN `product_attribute` ON `product`.`id` = `product_attribute`.`product_id`
WHERE `product`.`active` = '1' AND `product`.`url` IS NOT NULL
AND (`product_attribute`.`attribute_id` = 8 OR `product_attribute`.`attribute_id` IS NULL)
AND `product`.`provider_id` = '7' ORDER BY RAND() LIMIT 8
I was trying with left, inner and right join and nothing works.
You should put the condition for the left-joined table in the join, not the where clause
...
from product
left join provider ON product.provider_id = provider.id
left join product_attribute on product.id = product_attribute.product_id
and product_attribute.attribute_id = 8
where `product`.`active` = '1'
and `product`.`url` IS NOT NULL
and `product`.`provider_id` = '7'
...

trouble in find child field from primary field in mysql

I have two table like below
CREATE TABLE IF NOT EXISTS `countries` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=196 ;
ANd ANother one
CREATE TABLE IF NOT EXISTS `students` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`admission_no` varchar(255) DEFAULT NULL,
`nationality_id` int(11) DEFAULT NULL,
`country_id` int(11) DEFAULT NULL,
`is_active` tinyint(1) DEFAULT '1',
`is_deleted` tinyint(1) DEFAULT '0',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `admission_no` (`admission_no`)
) ENGINE=InnoDB DEFAULT CHARSET=latin
1 AUTO_INCREMENT=2 ;
So the problem is i want fetch both nationality_id,country_id name from countries table for this im have to use LEFT JOIN query so in this case i am facing problem as im getting same name for both if nationality_id,country_id are different as i can only join on one table only so could someone plz help me to solve this.
If I understand you correctly, you can achieve this by LEFT JOINING the same table twice, using aliases.
Something like
SELECT *
FROM students s LEF TJOIN
countries c ON s.country_id = c.id LEFT JOIN
countries n ON s.nationality_id = n.id
#astander there is a little bug in your query (second alias for countries n is not used in on statement). here is a correct statement.
select s.Id, cNationality.Name, cCountry.Name
from Students as s
left outer join Countries as cNationality on cNationality.Id = s.Nationality_id
left outer join Countries as cCountry on cCountry.Id = s.Country_id