the group by in mysql5.7 - mysql

I find the same sql run in mysql5.7 and mysql5.5 but the result is different.
the sql like this:
SELECT t2.* FROM (SELECT t1.* FROM t_user t1 ORDER BY t1.id desc) AS t2 GROUP BY t2.type;
The result in mysql5.7:
The result in mysql5.5:
the sql script like this:
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
`gender` varchar(255) DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`birth` datetime DEFAULT NULL,
`is_delete` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8;
INSERT INTO `t_user` VALUES ('1', 'James', '0594-5397864', '0', '3', '2016-01-30 19:01:09', '1');
INSERT INTO `t_user` VALUES ('2', 'Hayes', '0594-5392419', '1', '4', '2015-12-24 11:12:27', '1');
INSERT INTO `t_user` VALUES ('3', 'Diana', '0594-5393520', '1', '5', '2016-03-21 13:03:50', '0');
INSERT INTO `t_user` VALUES ('4', 'Rajah', '0594-5399812', '1', '4', '2015-11-26 02:11:35', '0');
INSERT INTO `t_user` VALUES ('5', 'Daria', '0594-5397571', '0', '4', '2016-01-18 11:01:11', '1');
INSERT INTO `t_user` VALUES ('6', 'Lee', '0594-5394539', '1', '1', '2015-10-23 08:10:23', '1');
INSERT INTO `t_user` VALUES ('7', 'Cameran', '0594-5392867', '0', '4', '2016-11-16 12:11:08', '0');
INSERT INTO `t_user` VALUES ('8', 'Wylie', '0594-5395349', '0', '5', '2017-07-06 04:07:27', '0');
INSERT INTO `t_user` VALUES ('9', 'Bertha', '0594-5395287', '1', '1', '2017-02-08 12:02:45', '1');
INSERT INTO `t_user` VALUES ('10', 'Fletcher', '0594-5399246', '0', '4', '2015-09-03 20:09:33', '0');
INSERT INTO `t_user` VALUES ('11', 'Conan', '0594-5391546', '1', '5', '2017-05-15 09:05:23', '0');
INSERT INTO `t_user` VALUES ('12', 'Raymond', '0594-5399666', '0', '3', '2015-10-20 05:10:05', '1');
INSERT INTO `t_user` VALUES ('13', 'Noel', '0594-5397392', '1', '4', '2017-05-26 03:05:56', '0');
INSERT INTO `t_user` VALUES ('14', 'Miriam', '0594-5399081', '0', '2', '2016-05-21 02:05:09', '0');
INSERT INTO `t_user` VALUES ('15', 'Maya', '0594-5397242', '0', '3', '2016-10-24 02:10:50', '1');
INSERT INTO `t_user` VALUES ('16', 'Winifred', '0594-5395142', '1', '1', '2017-03-15 02:03:43', '0');
INSERT INTO `t_user` VALUES ('17', 'Elaine', '0594-5398478', '1', '3', '2017-03-08 15:03:03', '1');
INSERT INTO `t_user` VALUES ('18', 'Robert', '0594-5397830', '0', '5', '2016-02-10 22:02:06', '0');
INSERT INTO `t_user` VALUES ('19', 'Patrick', '0594-5396516', '0', '4', '2015-09-10 07:09:51', '0');
INSERT INTO `t_user` VALUES ('20', 'Darrel', '0594-5397417', '0', '1', '2016-03-11 11:03:36', '0');
INSERT INTO `t_user` VALUES ('21', 'Salvador', '0594-5399732', '1', '3', '2016-01-01 15:01:21', '0');
INSERT INTO `t_user` VALUES ('22', 'Brandon', '0594-5396204', '1', '4', '2016-05-12 06:05:40', '1');
INSERT INTO `t_user` VALUES ('23', 'Dorothy', '0594-5396783', '0', '1', '2016-12-12 10:12:59', '1');
INSERT INTO `t_user` VALUES ('24', 'Kevyn', '0594-5398240', '0', '2', '2016-02-07 04:02:14', '1');
INSERT INTO `t_user` VALUES ('25', 'Brody', '0594-5398774', '1', '1', '2016-12-11 20:12:36', '0');
what cause this different... my English is poor,LOL...

MySQL is too liberal when it comes to data integrity and its default settings often allow you to accomplish data loss without a complaint (see reference for ONLY_FULL_GROUP_BY SQL mode). For instance, it allows incomplete GROUP BY clauses like yours and just picks an arbitrary (not even random) result.
You need to rewrite your query so all columns involved fit into one category:
Be part of an aggregate expression in the SELECT clause:
SELECT MAX(foo) AS maximum_foo
Be part of the GROUP BY clause:
SELECT foo
...
GROUP BY foo
This way you are being specific enough so the result-set is deterministic. Your current code is basically: "Give me one user of each type"—but you don't specify any criteria to pick users. Other DBMSs (Oracle, SQL Server) will complaint: "You didn't say how to choose users". MySQL will merely pick an arbitrary user (but not even a random user, because randomness would imply that chosen users follow a rule, which is not the case). The ONLY_FULL_GROUP_BY SQL mode is just a way to make MySQL behave like other engines.
You can also get rid of your subquery, it really serves no purpose.

No order on outer SELECT causes the difference. However what do you want to achieve? Take only one (which one) from each group (type) or what?
SELECT *
FROM t_user
WHERE id IN (SELECT MAX(id) FROM t_user GROUP BY type)
ORDER BY type
SQL fiddle link

Related

Absenteeism query showing date as employee is absent

I'm trying to generate an absenteeism query with the date employees were absent.
Table "ci_admin":
DROP TABLE IF EXISTS `ci_admin`;
CREATE TABLE `ci_admin` (
`admin_id` int(11) NOT NULL AUTO_INCREMENT,
`admin_role_id` int(11) NOT NULL,
`department_id` int(11) NOT NULL,
`firstname` varchar(255) NOT NULL,
`lastname` varchar(255) NOT NULL,
`emp_photo` varchar(255) NOT NULL,
PRIMARY KEY (`admin_id`)
);
INSERT INTO `ci_admin` VALUES ('1', '20170079320', '1', '1', 'AHMED', 'MAKOKHA', '125.jpg'), ('2', '20170079321', '1', '1', 'MARIAM', 'AHMED', '2.png'), ('3', '20170080091', '2', '3', 'SALAHUDEEN', 'AHMED', '3.png'), ('4', '20130080083', '2', '2', 'NURU', 'ABBAS', '4.png'), ('5', '20170080092', '5', '1', 'HIRBO', 'ALI', '5.png');
Table "ci_department":
DROP TABLE IF EXISTS `ci_department`;
CREATE TABLE `ci_department` (
`department_id` int(11) NOT NULL AUTO_INCREMENT,
`admin_id` int(11) NOT NULL,
`department_name` varchar(225) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`department_id`)
);
INSERT INTO `ci_department` VALUES ('1', 'INFORMATION COMMUNICATION TECHNOLOGY'), ('2', 'HUMAN RESOURCE'), ('3', 'PROCUREMENT');
Table "ci_admnin_roles":
DROP TABLE IF EXISTS `ci_admin_roles`;
CREATE TABLE `ci_admin_roles` (
`admin_role_id` int(11) NOT NULL AUTO_INCREMENT,
`admin_role_title` varchar(30) CHARACTER SET utf8 NOT NULL,
PRIMARY KEY (`admin_role_id`)
);
INSERT INTO `ci_admin_roles` VALUES ('1', 'Super Admin'), ('2', 'ADMIN'), ('3', 'ACCOUNTANT'), ('4', 'OPERATOR'), ('5', 'IT OFFICER'), ('6', 'APPLICANT');
Table "ci_attendance":
DROP TABLE IF EXISTS `ci_attendance`;
CREATE TABLE `ci_attendance` (
`attendance_id` int(11) NOT NULL AUTO_INCREMENT,
`attendance_date` date NOT NULL,
`admin_id` int(11) NOT NULL,
`check_in` time NOT NULL,
`check_out` time NOT NULL,
PRIMARY KEY (`attendance_id`)
);
INSERT INTO `ci_attendance` VALUES ('1', '2022-12-22', '1', '08:15:00', '18:30:00'), ('2', '2022-12-22', '2', '09:00:00', '18:30:00'), ('3', '2022-12-22', '3', '10:00:00', '19:30:00'), ('4', '2020-02-21', '1', '10:30:00', '20:30:00'), ('5', '2023-12-12', '2', '10:00:00', '21:30:00'), ('6', '2023-12-23', '3', '10:00:00', '22:30:00'), ('7', '2023-12-22', '3', '08:00:00', '17:30:00'), ('8', '2021-02-21', '2', '09:00:00', '18:30:00'), ('9', '2024-12-12', '3', '10:00:00', '19:30:00'), ('10', '2024-12-23', '1', '10:00:00', '20:30:00'), ('11', '2024-12-22', '2', '10:00:00', '21:30:00'), ('12', '2022-02-21', '3', '10:00:00', '22:30:00'), ('13', '2022-12-12', '1', '08:00:00', '17:30:00'), ('14', '2022-12-23', '3', '09:00:00', '18:30:00'), ('15', '2022-12-02', '2', '10:00:00', '19:30:00'), ('16', '2020-02-21', '1', '09:45:00', '22:00:00'), ('17', '2023-12-12', '2', '10:00:00', '21:30:00'), ('18', '2020-12-23', '3', '10:00:00', '22:30:00'), ('19', '2023-12-22', '1', '08:00:00', '17:30:00'), ('20', '2021-02-21', '2', '09:00:00', '18:30:00'), ('21', '2024-12-03', '2', '10:00:00', '19:30:00'), ('22', '2021-12-23', '1', '10:00:00', '20:30:00'), ('23', '2024-10-22', '3', '10:00:00', '21:30:00'), ('24', '2022-02-21', '3', '10:00:00', '22:30:00'), ('25', '2022-12-12', '1', '08:00:00', '17:30:00'), ('26', '2022-09-23', '2', '09:00:00', '18:30:00'), ('27', '2022-12-22', '4', '10:00:00', '19:30:00'), ('28', '2020-02-21', '1', '10:00:00', '20:30:00'), ('29', '2023-04-12', '5', '10:00:00', '21:30:00'), ('30', '2023-12-03', '4', '10:00:00', '22:30:00'), ('31', '2023-12-22', '1', '08:00:00', '17:30:00'), ('32', '2021-02-21', '5', '09:00:00', '18:30:00'), ('33', '2024-12-12', '4', '10:00:00', '19:30:00');
My current MySQL query which displays employees who are absent does not include the date when the employee is absent:
SELECT ci_admin.emp_no,
ci_admin.firstname,
ci_admin.lastname,
ci_admin.emp_photo,
ci_department.department_name,
ci_admin_roles.admin_role_title
FROM ci_admin
INNER JOIN ci_department ON ci_department.department_id = ci_admin.department_id
INNER JOIN ci_admin_roles on ci_admin_roles.admin_role_id=ci_admin.admin_role_id
WHERE ci_admin.is_deleted = 0
AND ci_admin.admin_id NOT IN (SELECT ci_attendance.admin_id
FROM ci_attendance
WHERE ci_attendance.attendance_date BETWEEN '2023-01-01' AND '2023-01-31')
ORDER BY ci_admin.department_id
I want the specific date that an employee is absent to be included in my query. How can I modify my query?

Show filters from category & products, show filters relevant filter post selection (and non-relevant from active parent) & product count per filter

I posted a similar question on 10 Jan 2020 (see here) but my problem/question has expanded since then and don't want S.O. police on my back ;)
This is the criteria:
There are filter categories and there are filters belong to those categories.
Each filter displays a count of how many products belong to a filter.
The number of available filters need to reduce depending on active filter selection to not only those that still apply to the pool of products but those that are also relevant to child filters of other filter categories.
There is a caveat to the above, if only one filter category has child filters selected then all the sibling filters (filters of the same filter category are to still display) until a filter from another filter category is made active. Once a filter from another filter category/parent is selected the previous full list can reduce but previously select filters must remain active just show a count of zero if that's the case.
I think I'm really close with the SQL below but it will not correctly update the count of products belonging to each filter upon selection. The only way I can get it to work is if I change the OR to an AND in the SQL below but this then will not show the other child filters belonging to a filter category that has an active child filter.
I have provided schema and sample data here, along with a fiddle of samehttp://sqlfiddle.com/#!9/afbd4d7/1/0:
CREATE TABLE `store_categories` (
`categories_id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`categories_id`),
KEY `idx_categories_parent_id` (`parent_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `store_products` (
`products_id` int(11) NOT NULL AUTO_INCREMENT,
`products_status` tinyint(1) NOT NULL,
PRIMARY KEY (`products_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `store_products_filters` (
`pf_id` int(11) NOT NULL AUTO_INCREMENT,
`pf_name` varchar(250) COLLATE utf8_unicode_ci DEFAULT NULL,
`pf_sort` int(4) DEFAULT '0',
`pf_to_pfc_id` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`pf_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `store_products_filters_categories` (
`pfc_id` int(11) NOT NULL AUTO_INCREMENT,
`pfc_name` varchar(250) COLLATE utf8_unicode_ci DEFAULT NULL,
`pfc_sort` int(4) DEFAULT '0',
PRIMARY KEY (`pfc_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `store_products_filters_to_products` (
`pftp_id` int(11) NOT NULL AUTO_INCREMENT,
`pftp_pf_id` int(11) DEFAULT NULL,
`pftp_products_id` int(11) DEFAULT NULL,
PRIMARY KEY (`pftp_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `store_products_to_categories` (
`products_id` int(11) NOT NULL,
`categories_id` int(11) NOT NULL,
PRIMARY KEY (`products_id`,`categories_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `store_categories` VALUES ('1', '0');
INSERT INTO `store_categories` VALUES ('2', '1');
INSERT INTO `store_categories` VALUES ('3', '0');
INSERT INTO `store_categories` VALUES ('4', '1');
INSERT INTO `store_categories` VALUES ('5', '0');
INSERT INTO `store_categories` VALUES ('6', '0');
INSERT INTO `store_products` VALUES ('1', '1');
INSERT INTO `store_products` VALUES ('2', '1');
INSERT INTO `store_products` VALUES ('3', '1');
INSERT INTO `store_products` VALUES ('4', '1');
INSERT INTO `store_products` VALUES ('5', '0');
INSERT INTO `store_products_filters` VALUES ('1', 'Small', '0', '1');
INSERT INTO `store_products_filters` VALUES ('2', 'Medium', '0', '1');
INSERT INTO `store_products_filters` VALUES ('3', 'Large', '0', '1');
INSERT INTO `store_products_filters` VALUES ('4', 'Extra Large', '0', '1');
INSERT INTO `store_products_filters` VALUES ('5', 'Circle', '0', '2');
INSERT INTO `store_products_filters` VALUES ('6', 'Square', '0', '2');
INSERT INTO `store_products_filters` VALUES ('7', 'Triangle', '0', '2');
INSERT INTO `store_products_filters` VALUES ('8', 'Red', '5000', '3');
INSERT INTO `store_products_filters` VALUES ('9', 'Blue', '5000', '3');
INSERT INTO `store_products_filters` VALUES ('10', 'Black', '2000', '3');
INSERT INTO `store_products_filters` VALUES ('11', 'Yellow', '5000', '3');
INSERT INTO `store_products_filters_categories` VALUES ('1', 'Size', '300');
INSERT INTO `store_products_filters_categories` VALUES ('2', 'Type', '100');
INSERT INTO `store_products_filters_categories` VALUES ('3', 'Colour', '400');
INSERT INTO `store_products_filters_to_products` VALUES ('1', '1', '1');
INSERT INTO `store_products_filters_to_products` VALUES ('2', '6', '1');
INSERT INTO `store_products_filters_to_products` VALUES ('3', '9', '1');
INSERT INTO `store_products_filters_to_products` VALUES ('4', '1', '2');
INSERT INTO `store_products_filters_to_products` VALUES ('5', '5', '2');
INSERT INTO `store_products_filters_to_products` VALUES ('6', '8', '2');
INSERT INTO `store_products_filters_to_products` VALUES ('7', '2', '3');
INSERT INTO `store_products_filters_to_products` VALUES ('8', '7', '3');
INSERT INTO `store_products_filters_to_products` VALUES ('9', '11', '3');
INSERT INTO `store_products_filters_to_products` VALUES ('10', '3', '4');
INSERT INTO `store_products_filters_to_products` VALUES ('11', '5', '4');
INSERT INTO `store_products_filters_to_products` VALUES ('12', '10', '4');
INSERT INTO `store_products_to_categories` VALUES ('1', '2');
INSERT INTO `store_products_to_categories` VALUES ('2', '2');
INSERT INTO `store_products_to_categories` VALUES ('3', '2');
INSERT INTO `store_products_to_categories` VALUES ('4', '2');
INSERT INTO `store_products_to_categories` VALUES ('5', '2');
There are 3 active filters, Small, Medium and Circle.
There are 2 "Small" sized products
There is 1 "Medium" sized product
There are 2 products that have a "Circle" filter applied to them but only one of those is "Small" and the other is "Large"
With Small, Medium filters applied I would expect/like Circle to show 1 but it shows a count of 2
OR pf.pf_id IN (
SELECT
pf_id
FROM
store_products_filters
WHERE
pf_to_pfc_id IN (1, 2)
)
My thought so far is to remove the above SQL if filters from more than one filter category are active.
You can see an example of what I'm trying to achieve on this site https://www.ukpos.com/display-stands-frames-and-noticeboards which is powered by Magento. I'm not sure if the filters are out of the box or a added plugin.
My full SQL below.
SELECT
pfc.pfc_id,
pfc.pfc_name,
pf.pf_id,
pf.pf_name,
count(pftp.pftp_pf_id) AS products_in_filter
FROM
store_products_filters_to_products pftp
LEFT JOIN store_products_filters pf ON pftp.pftp_pf_id = pf.pf_id
LEFT JOIN store_products_filters_categories pfc ON pf.pf_to_pfc_id = pfc_id
WHERE
pftp_products_id IN (
SELECT
ptc.products_id
FROM
store_products_to_categories ptc
LEFT JOIN store_products p ON ptc.products_id = p.products_id
WHERE
p.products_status = 1
AND (
ptc.categories_id = 1
OR ptc.categories_id IN (
SELECT
categories_id
FROM
store_categories
WHERE
parent_id = '1'
)
)
)
AND (
pf.pf_id IN (
SELECT
pftp_pf_id
FROM
store_products_filters_categories
WHERE
pftp_products_id IN (
SELECT
pftp.pftp_products_id
FROM
store_products_filters_to_products pftp
JOIN store_products_filters pf ON pf.pf_id = pftp.pftp_pf_id
GROUP BY
pftp.pftp_products_id
HAVING
(
sum(pf.pf_id = 1) > 0
OR sum(pf.pf_id = 2) > 0
)
AND (sum(pf.pf_id = 5) > 0)
)
)
OR pf.pf_id IN (
SELECT
pf_id
FROM
store_products_filters
WHERE
pf_to_pfc_id IN (1, 2)
)
)
GROUP BY
pfc.pfc_id,
pftp.pftp_pf_id
ORDER BY
pfc.pfc_sort ASC,
pfc.pfc_name ASC,
pf.pf_sort ASC,
pf.pf_name ASC

How to find start time of last GPS route

I have data from GPS trackers.
Let's say something like:
CREATE TABLE IF NOT EXISTS `tab_gps` (
`id` int(6) unsigned NOT NULL,
`speed` int(3) unsigned NOT NULL,
`time` time NOT NULL,
`tracker_name` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
and some sample data:
('1', '5', '07:00', 'car'),
('2', '10', '07:10', 'car'),
('3', '0', '07:20', 'bus'),
('4', '0', '07:30', 'car'),
('5', '0', '07:40', 'car'),
('6', '0', '07:50', 'car'),
('7', '20', '08:00', 'car'),
('8', '40', '08:10', 'bus'),
('9', '15', '08:15', 'car'),
('10', '0', '08:32', 'car'),
('11', '0', '08:40', 'car'),
('12', '0', '08:52', 'bus'),
('13', '12', '09:10', 'car'),
('14', '0', '09:25', 'car'),
('15', '0', '09:30', 'car'),
('16', '0', '09:35', 'car'),
('17', '10', '09:41', 'car'),
('18', '5', '09:46', 'car');
('19', '0', '09:50', 'car');
The question is how to find the time when specific 'tracker_name' started his route
So in my example algorithm in my head is:
SELECT * FROM tab_gps WHERE tracker_name='car' then
find the last position with speed=0 (which have next positions >0)
and the next position is the TIME which I am looking for
In my example, it is a position time: 09:41 (id = 17)
I am trying to use:
select max(time) from tab_gps where tracker_name='car' and speed <>0
Here is the Fiddle to better understanding: http://sqlfiddle.com/#!9/834371
Well, for the final route, you can do:
select g.*
from tab_gp2 g
where g.tracker_name = 'car' and
g.id > (select coalesce(max(g2.id), -1)
from tab_gps g2
where g2.speed = 0 and g2.tracker_name = 'car'
);
Here is a db<>fiddle illustrating the logic.

Having problems with getting a count in a select statement

I have 5 tables as follows:
files
tags
profiles
j_file_tags
j_profile_tags
So files can have tags, profiles give access to certain tags.
I put together two queries that do the following:
get a list of files that a specific profile has access to (the profile must have access to all tags a file might have)
get a list of tags that the profile has access to and where there is at least a file in that tag.
What I need for query 2 is a count of how many files are in a tag.
Here's the table structure and sample data:
CREATE TABLE `files` (
`id` int(4) NOT NULL AUTO_INCREMENT ,
`fileName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`empID` int(4) NOT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=Dynamic;
CREATE TABLE `j_file_tags` (
`id` int(4) NOT NULL AUTO_INCREMENT ,
`fileID` int(4) NULL DEFAULT NULL ,
`tagID` int(4) NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=Dynamic;
CREATE TABLE `j_profile_tags` (
`id` int(4) NOT NULL AUTO_INCREMENT ,
`profileID` int(4) NULL DEFAULT NULL ,
`tagID` int(4) NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=Dynamic;
CREATE TABLE `profiles` (
`id` int(4) NOT NULL AUTO_INCREMENT ,
`profileName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=Dynamic;
CREATE TABLE `tags` (
`id` int(4) NOT NULL AUTO_INCREMENT ,
`tagName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=Dynamic;
INSERT INTO `files` VALUES ('1', 'FileOne', '1');
INSERT INTO `files` VALUES ('2', 'FileTwo', '1');
INSERT INTO `files` VALUES ('3', 'FileThree', '1');
INSERT INTO `files` VALUES ('4', 'FileFour', '2');
INSERT INTO `files` VALUES ('5', 'FileFive', '2');
INSERT INTO `files` VALUES ('6', 'FileSix', '2');
INSERT INTO `files` VALUES ('7', 'FileSeven', '2');
INSERT INTO `profiles` VALUES ('1', 'ProfileOne');
INSERT INTO `profiles` VALUES ('2', 'ProfileTwo');
INSERT INTO `profiles` VALUES ('3', 'ProfileThree');
INSERT INTO `tags` VALUES ('1', 'TagOne');
INSERT INTO `tags` VALUES ('2', 'TagTwo');
INSERT INTO `tags` VALUES ('3', 'TagThree');
INSERT INTO `tags` VALUES ('4', 'TagFour');
INSERT INTO `tags` VALUES ('5', 'TagFive');
INSERT INTO `j_file_tags` VALUES ('1', '1', '1');
INSERT INTO `j_file_tags` VALUES ('2', '1', '3');
INSERT INTO `j_file_tags` VALUES ('3', '2', '1');
INSERT INTO `j_file_tags` VALUES ('4', '2', '5');
INSERT INTO `j_file_tags` VALUES ('5', '3', '1');
INSERT INTO `j_file_tags` VALUES ('6', '3', '3');
INSERT INTO `j_file_tags` VALUES ('7', '3', '6');
INSERT INTO `j_file_tags` VALUES ('8', '2', '3');
INSERT INTO `j_file_tags` VALUES ('9', '4', '1');
INSERT INTO `j_file_tags` VALUES ('10', '4', '2');
INSERT INTO `j_file_tags` VALUES ('11', '5', '1');
INSERT INTO `j_file_tags` VALUES ('12', '5', '6');
INSERT INTO `j_profile_tags` VALUES ('1', '1', '2');
INSERT INTO `j_profile_tags` VALUES ('2', '1', '3');
INSERT INTO `j_profile_tags` VALUES ('3', '1', '4');
INSERT INTO `j_profile_tags` VALUES ('4', '2', '1');
INSERT INTO `j_profile_tags` VALUES ('5', '2', '2');
INSERT INTO `j_profile_tags` VALUES ('6', '2', '3');
INSERT INTO `j_profile_tags` VALUES ('7', '2', '4');
INSERT INTO `j_profile_tags` VALUES ('8', '2', '5');
INSERT INTO `j_profile_tags` VALUES ('9', '1', '1');
INSERT INTO `j_profile_tags` VALUES ('10', '1', '5');
Here are my 2 queries:
/* Get list of files: limit by specific employee AND by tags the use has access to */
SELECT
`files`.`id`,
`files`.`fileName`,
`files`.`empID`,
GROUP_CONCAT(CONCAT(`tags`.`id`) SEPARATOR ', ') as `FileTags`
FROM `files`
LEFT JOIN `j_file_tags` ON `j_file_tags`.`fileID` = `files`.`id`
LEFT JOIN `tags` ON `tags`.`id` = `j_file_tags`.`tagID`
WHERE
`files`.`empID` = 1
AND
`j_file_tags`.`tagID` IN (1,2,3,4,5)
GROUP BY
`files`.`id`
HAVING
COUNT(`j_file_tags`.`id`) = (SELECT COUNT(`j_file_tags`.`id`) FROM `j_file_tags` WHERE `j_file_tags`.`fileID` = `files`.`id` );
/* SECOND QUERY where i need help */
SELECT
`tags`.`id`,
`tags`.`tagName`,
'1' as `fileCount` /* i need this to be an actual count */
FROM
`tags`
LEFT JOIN `j_file_tags` ON `j_file_tags`.`tagID` = `tags`.`id`
LEFT JOIN `files` ON `files`.`id` = `j_file_tags`.`fileID`
LEFT JOIN `j_profile_tags` ON `j_profile_tags`.`tagID` = `tags`.`id`
WHERE
`j_profile_tags`.`profileID` = 1
AND
`files`.`empID` = 1
GROUP BY
`tags`.`id`;
/* the fileCount column would need to say for TagOne - 2, for TagThree - 2 and for TagFive - 1 */
In the sample data the first query returns:
| 1 | FileOne | 1 | 1, 3
| 2 | FileTwo | 1 | 3, 1, 5
The second query returns:
| 1 | TagOne | should return 2
| 3 | TagThree | should return 2
| 5 | TagFive| | should return 1
The answer is simple the HAVING clause that you have in the first query is throwing off your results because there are truly 3 files with tags 1 and 3. I would suggest removing it all together or figuring out another way to do it. I rewrote your query below to display your desired result, however it is not a fix all.
SELECT
tags.id,
tags.tagName,
count(fileID) as 'fileCount'
FROM files
LEFT JOIN j_file_tags ON j_file_tags.fileID = files.id
LEFT JOIN tags ON tags.id = j_file_tags.tagID
WHERE
files.empID = 1
AND
j_file_tags.tagID IN (1,2,3,4,5)
AND
fileID IN (1,2)
GROUP BY
tags.id

MySQL: Cross table Referencing (primary, master, name) for categories and subcategories?

I am creating a table labeled categories where main catgory(parent column) contains 0 and where subcategory contains the ID of the parent category. I heard it is called Referencing. My question: Do i have this table strucutred properly? Or is there a better way like implementing a traversal tree or similar method?
CREATE TABLE `categories` (
`primary_id` int(11) NOT NULL auto_increment,
`master_id` int(11) NOT NULL default '0',
`name` varchar(50) NOT NULL default '',
PRIMARY KEY (`c_id`)
)
INSERT INTO `categories` (`primary_id`, `master_id`, `name`) VALUES
(1, '0', 'Games'),
(2, '0', 'Technology'),
(3, '0', 'Science'),
(4, '0', 'Pop Culture'),
(5, '0', 'Jobs/Services'),
(6, '0', 'Blogs'),
(7, '1', 'Computer'),
(8, '1', 'Game Console'),
(9, '2', 'Cellphone'),
(10, '2', 'Audio/Video/Photos'),
(11, '2', 'Laptop'),
(12, '2', 'Others >'),
(13, '3', 'Human Body'),
(14, '3', 'Ocean Life'),
(15, '3', 'Plant Life'),
(16, '3', 'Animal Life'),
(17, '4', 'Artist'),
(18, '4', 'Upcoming'),
(19, '5', 'Jobs'),
(20, '5', 'Tutoring'),
(21, '5', 'Seminars'),
(22, '5', 'Haircuts'),
(23, '9', 'Iphone'),
(24, '9', 'Android'),
(25, '9', 'Windows Mobile'),
(26, '11', 'Toshiba'),
(27, '11', 'HP'),
(28, '11', 'Apple'),
If you use the InnoDB engine, you can enforce referential integrity for your tables. That way, you can make sure you never insert a category which does not have matching parent id, cascade delete child categories when you delete the parent, etc. Change your create statement to:
CREATE TABLE `categories` (
`primary_id` int(11) NOT NULL auto_increment,
`master_id` int(11) NOT NULL default '0',
`name` varchar(50) NOT NULL default '',
PRIMARY KEY (`c_id`),
KEY `fk_master_id1` (`master_id`),
CONSTRAINT `fk_master_id1` FOREIGN KEY (`master_id`) REFERENCES `master` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
While this strategy works sufficiently well for most cases, you might also want to look at other strategies for implementing tree-like structures and the challenges that come with querying them effectively. See this answer on SO for more information on alternative designs for storing tree-like structures in MySQL.