Related
I have two relational tables, and I would like to filter data using IF condition. The problem is that using LEFT JOIN I got records that cannot be grouped.
The tables that I have are:
calendar
bookers
The first table consists of lessons that can be booked by more people, and the second table contains data who booked each lesson. The IF condition that I would like to implement is: return '2' if lesson is booked by specific user, return '1' if lesson is booked, but by another user, and return '0' if lesson is not booked.
What I would like to get according to above tables is given in the figure below.
Expected result
But, when I use LEFT JOIN to link those tables, I got record for every user that booked specific lesson.
SELECT calendar.id, calendarId, lessonType, description,
CASE
WHEN bookedBy then IF(bookedBy = 8, '2', '1')
ELSE '0'
END AS bb,
(select count(bookedBy) from bookers where calendar.id = bookers.lessonId) as nOfBookers
FROM calendar
LEFT JOIN bookers ON calendar.id = bookers.lessonId
WHERE `calendarId`= 180
Without the LEFT JOIN (fiddle), counts are shown properly, but I cannot include IF condition, because the table bookers is not defined.
I would appreciate any help. Thank you very much in advance.
Here is the Fiddle.
CREATE TABLE `calendar` (
`id` int(11) NOT NULL,
`calendarId` varchar(50) NOT NULL,
`lessonType` varchar(255) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `calendar`
(`id`, `calendarId`, `lessonType`, `description`)
VALUES
(1, '180', 'A', ''),
(2, '180', 'A', ''),
(3, '180', 'A', ''),
(4, '180', 'B', ''),
(5, '180', 'B', ''),
(6, '180', 'B', ''),
(7, '180', 'B', ''),
(8, '180', 'B', ''),
(9, '180', 'B', '');
CREATE TABLE `bookers` (
`id` int(11) NOT NULL,
`lessonId` int(11) DEFAULT NULL,
`bookedBy` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
--
-- Dumping data for table `bookers`
--
INSERT INTO `bookers` (`id`, `lessonId`, `bookedBy`) VALUES
(4, 1, 8),
(5, 2, 8),
(6, 2, 28),
(7, 2, 17),
(8, 3, 11);
--
-- Indexes for dumped tables
--
ALTER TABLE `calendar`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `id` (`id`);
--
-- Indexes for table `bookers`
--
ALTER TABLE `bookers`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `bookers`
--
ALTER TABLE `bookers`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=9;
COMMIT;
select version();
Try this:
SELECT id, calendarid, lessontype, description,
CASE WHEN FIND_IN_SET(8,vbb)>0 THEN 2
WHEN vbb IS NOT NULL THEN 1
ELSE 0 END AS bb,
nOfBookers
FROM
(SELECT c.id, calendarId, lessonType, GROUP_CONCAT(bookedby) AS vbb, description,
(SELECT COUNT(bookedby) FROM bookers WHERE c.id = bookers.lessonId) AS nOfBookers
FROM calendar c
LEFT JOIN bookers b ON c.id = b.lessonId
WHERE `calendarId`= 180
GROUP BY c.id, calendarId, lessonType, description) A;
In addition to your original LEFT JOIN attempt, I've added GROUP_CONCAT(bookedby) AS vbb which will return a comma separated bookedby value; which is 17,28,8. After that, I make the query as a sub-query and do CASE expression with FIND_IN_SET function on vbb to look for specific bookedby.
Here's an update fiddle: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=0933e9fc3cb7445311c34c6705d11637
The problem:
When I update Tom in the redo table, I want the changes to affect the daily and lab irrespective of whether Tom exists in daily or lab. Even if he doesn't exist in daily or
lab I want the update to be made only in the redo table.
Look at the tables below:
First table redo
Second Table daily
Third Table lab
What I tried:
UPDATE redo,daily,lab SET
redo.name = '$newName', daily.name = '$newName', lab.name = '$newName',
redo.place = '$newPlace', daily.place = '$newPlace', lab.place = '$newPlace',
redo.code = '$newCode', daily.code = '$newCode', lab.code = '$newCode',
redo.age = '$newAge', daily.age= '$newAge', lab.age = '$newAge',
redo.date = redo.date, daily.date = daily.date, lab.date = lab.date,
redo.contact = '$newContact',daily.contact = '$newContact', lab.contact='$newContact',
redo.secondarycontact = '$newSecondaryContact',
daily.secondarycontact = '$newSecondaryContact',
lab.secondarycontact = '$newSecondaryContact'
WHERE redo.no='$no' AND
(redo.name=daily.name AND redo.name=lab.name) AND
(redo.place=daily.place AND redo.place=lab.place)
Result:
Values are updated only if they exist in all the 3 tables at the same time.
Given your use case and relationships, this is how I would set up the tables if it were my project:
NOTE:
I've made some assumptions about your field types in the below that may not be correct, but it should be trivial to adapt them as needed for your use case.
Using this structure will require slightly more complex or well thought out queries, but the result will be MUCH more maintainable and far less error prone.
I would normaly name columns and such differently, but I've elected to use names similar to the naming convention you seem to be using to keep it simple since this question isnt about "best practices naming columns" etc..
Create the tables
Create an 'activities' table to hold all types of activities in one place, note the "type" column with possible values of 'lab','redo','daily'
CREATE TABLE `activities` (
`no` bigint(20) NOT NULL AUTO_INCREMENT,
`consultation` varchar(255) DEFAULT NULL,
`lab` varchar(255) DEFAULT NULL,
`token` VARCHAR(45) NULL,
`detailsid` bigint(20) NOT NULL,
`foreignkey` bigint(20) DEFAULT NULL,
`type` enum('lab','redo','daily') NOT NULL,
`date` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`no`)
);
Create an 'activitiyusers' table to hold the details for the people that are related to our "activities"
CREATE TABLE `activitiyusers` (
`no` BIGINT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`gender` ENUM('M', 'F') NOT NULL,
`age` SMALLINT NULL,
`contact` VARCHAR(255) NOT NULL,
`secondarycontact` VARCHAR(255) NULL,
`place` VARCHAR(255) NULL,
`code` VARCHAR(45) NULL,
`createdat` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
`updatedat` VARCHAR(45) NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`no`));
Insert some test data
Insert 3 new "activitiyusers"
INSERT INTO `activitiyusers` (`name`, `gender`, `age`, `contact`, `secondarycontact`, `place`, `code`) VALUES ('Tom', 'M', '31', '1212121', '3434343', 'California', '1');
INSERT INTO `activitiyusers` (`name`, `gender`, `age`, `contact`, `secondarycontact`, `place`, `code`) VALUES ('Jack', 'M', '45', '99999', '11111', 'Colorado', '2');
INSERT INTO `activitiyusers` (`name`, `gender`, `age`, `contact`, `secondarycontact`, `place`, `code`) VALUES ('Harry', 'M', '99', '112233', '998877', 'Texas', '3');
Insert 3 new "redo" activities, one related to each of our 3 "activitiyusers"
INSERT INTO `activities` (`token`, `detailsid`, `foreignkey`, `type`) VALUES ('0', '1', NULL, 'redo');
INSERT INTO `activities` (`token`, `detailsid`, `foreignkey`, `type`) VALUES ('0', '2', NULL, 'redo');
INSERT INTO `activities` (`token`, `detailsid`, `foreignkey`, `type`) VALUES ('0', '3', NULL, 'redo');
Insert 2 new "daily" activities, one related to Tom and 'redo' activity 1, the second related to Harry and 'redo' activity 3"
INSERT INTO `activities` (`consultation`, `detailsid`, `foreignkey`, `type`) VALUES ('Cough and Cold', '1', '1', 'daily');
INSERT INTO `activities` (`consultation`, `detailsid`, `foreignkey`, `type`) VALUES ('Panadol', '3', '3', 'daily');
Insert 2 new "lab" activities, one related to Jack and 'redo' activity 2, the second related to Harry and 'redo' activity 3"
INSERT INTO `activities` (`lab`, `detailsid`, `foreignkey`, `type`) VALUES ('Blood Test', '2', '2', 'lab');
INSERT INTO `activities` (`lab`, `detailsid`, `foreignkey`, `type`) VALUES ('Injection', '3', '3', 'lab');
Examples of how to work with this data:
Note: these queries will make use of join cluases as well as Column and table aliases
Get All "activities" for "Tom", along with Tom's details
SELECT
a.no AS activityno,
a.consultation,
a.lab,
a.token,
a.type,
a.date,
au.no AS userno,
au.name,
au.gender,
au.age,
au.contact,
au.secondarycontact,
au.place,
au.code,
au.createdat,
au.updatedate
FROM
activities a
JOIN
activitiyusers au ON a.detailsid = au.no
WHERE
name = 'Tom';
Get all "redo type activities" for "Jack", along with Jack's details
SELECT
a.no AS activityno,
a.consultation,
a.lab,
a.token,
a.type,
a.date,
au.no AS userno,
au.name,
au.gender,
au.age,
au.contact,
au.secondarycontact,
au.place,
au.code,
au.createdat,
au.updatedate
FROM
activities a
JOIN
activitiyusers au ON a.detailsid = au.no
WHERE
name = 'Jack' AND a.type = 'redo';
# Given a known activity that has an id/no of '2',
# update the details for the activityuser related to that activity
UPDATE activitiyusers
SET
contact = '22222222',
age = 46,
code = 5
WHERE
no = (SELECT
detailsid
FROM
activities
WHERE
no = 2);
Given a known "repo" activity that has an id/no of '3', get all sub activities related to that activity along with details of the related activitiyuser
Note that this utilizes a mysql self join, ie we are joining the activities on itself to get subActivity rows that are related to a given redoActivity row.
SELECT
redoActivity.no AS redoactivityno,
subActivity.no AS subactivityno,
redoActivity.consultation AS redoactivityconsultation,
subActivity.consultation AS subactivityconsultation,
subActivity.lab,
redoActivity.token,
subActivity.type,
redoActivity.date AS redoactivitydate,
subActivity.date AS subactivitydate,
au.no AS userno,
au.name,
au.gender,
au.age,
au.contact,
au.secondarycontact,
au.place,
au.code,
au.createdat,
au.updatedate
FROM
activities subActivity
JOIN activities redoActivity ON subActivity.foreignkey = redoActivity.no
JOIN activitiyusers au ON redoActivity.detailsid = au.no
WHERE
redoActivity.no = 3;
I'm building an ecommerce website for fun with three main components: frontend in Angular, MySQL database and a PHP backend that will function as a REST API.
The inventory database consists of products of which there are many variants (Yes, I've seen many questions on StackExchange discussing these kind of issues but I havent found a satisfying soultion yet). After reading several horror stories of using an EAV approach, Im avoiding that.
I think I have made a database solution that works, but Im uncertain to how I should query the data. As there are several many-to-many relationships a query for 1 product and all its variants can return several rows where only a couple columns contain "new" data.
(TL:DR) Main question: When querying a product for all its variants (colors, sizes of available colors and quantity of a color/size variant), what is most efficient:
Perform 1 database query returning many rows where I would need to build an associative array in PHP, discarding "duplicate" data?
Perform several queries when retrieving all variants of a product?
Or is there a flaw in the database design which is the cause of these issues?
Database schema:
CREATE TABLE IF NOT EXISTS `products` (
`productID` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`longDescription` TEXT,
`shortDescription` VARCHAR(1000),
PRIMARY KEY (`productID`)
);
INSERT INTO `products` (`productID`, `name`, `longDescription`, `shortDescription`) VALUES
(1, 'A shirt', 'Long description of this product', 'shortDesc of shirt');
CREATE TABLE IF NOT EXISTS `productpricing` (
`productID` INT(11) NOT NULL,
`startDate` TIMESTAMP NOT NULL DEFAULT '1971-01-01 00:00:00',
`endDate` TIMESTAMP NOT NULL DEFAULT '2099-01-01 00:00:00',
`price` DECIMAL(10, 2) NOT NULL,
PRIMARY KEY(`productID`, `endDate`),
FOREIGN KEY (`productID`) REFERENCES products(`productID`)
);
INSERT INTO `productpricing` (`productID`, `startDate`, `endDate`, `price`) VALUES
(1, '1971-01-01 00:00:00', '2099-01-01 00:00:00', 309.99);
CREATE TABLE IF NOT EXISTS `categories` (
`categoryID` INT(11) NOT NULL AUTO_INCREMENT,
`categoryName` VARCHAR(100) NOT NULL,
PRIMARY KEY (`categoryID`)
);
INSERT INTO `categories` (`categoryID`, `categoryName`) VALUES
(1, 'Test Category');
CREATE TABLE IF NOT EXISTS `sizes` (
`sizeID` INT(11) NOT NULL,
`size` INT NOT NULL,
PRIMARY KEY (`sizeID`)
);
INSERT INTO `sizes` (`sizeID`, `size`) VALUES
(1, 50),
(2, 56),
(3, 62),
(4, 68),
(5, 74),
(6, 80),
(7, 86);
CREATE TABLE IF NOT EXISTS `colors` (
`colorID` INT(11) NOT NULL,
`color` VARCHAR(100) NOT NULL,
PRIMARY KEY (`colorID`)
);
INSERT INTO `colors` (`colorID`, `color`) VALUES
(1, "Red"),
(2, "White"),
(3, "Blue"),
(4, "Purple");
CREATE TABLE IF NOT EXISTS `product_variants` (
`productvariantID` INT(11) NOT NULL,
`productID` INT(11) NOT NULL,
`categoryID` INT(11) NOT NULL,
`colorID` INT(11) NOT NULL,
`sizeID` INT(11) NOT NULL,
`sku` VARCHAR(50) NOT NULL UNIQUE,
`quantity` INT(11) NOT NULL,
`isActive` BOOLEAN NOT NULL DEFAULT 0,
PRIMARY KEY (`productvariantID`),
UNIQUE (`productID`, `colorID`, `sizeID`),
FOREIGN KEY (`productID`) REFERENCES products(`productID`),
FOREIGN KEY (`categoryID`) REFERENCES categories(`categoryID`),
FOREIGN KEY (`colorID`) REFERENCES colors(`colorID`),
FOREIGN KEY (`sizeID`) REFERENCES sizes(`sizeID`)
);
INSERT INTO `product_variants` (`productvariantID`, `productID`, `categoryID`, `colorID`, `sizeID`, `sku`, `quantity`, `isActive`) VALUES
(1, 1, 1, 2, 1, 'clalb121', 2, 1),
(2, 1, 1, 3, 2, 'clalb132', 1, 1),
(3, 1, 1, 2, 2, 'clalb122', 5, 1);
CREATE TABLE IF NOT EXISTS `images` (
`imageID` INT(11) NOT NULL AUTO_INCREMENT,
`imageFilename` VARCHAR(100) NOT NULL,
PRIMARY KEY(`imageID`)
);
INSERT INTO `images` (`imageID`, `imageFilename`) VALUES
(1, 'shirtwhite1.jpg'),
(2, 'shirtwhite2.jpg'),
(3, 'shirtblue1.jpg'),
(4, 'shirtblue2.jpg');
CREATE TABLE IF NOT EXISTS `product_variant_images` (
`productvariantID` INT(11) NOT NULL,
`imageID` INT(11) NOT NULL,
FOREIGN KEY(`productvariantID`) REFERENCES product_variants(`productvariantID`),
FOREIGN KEY(`imageID`) REFERENCES images(`imageID`)
);
INSERT INTO `product_variant_images` (`productvariantID`, `imageID`) VALUES
(1, 1),
(1, 2),
(2, 3),
(2, 4),
(3, 1),
(3, 2);
(Im going to remove categoryID from the product_variants table as different variants of the same product does not have different categories.)
Example of querying 1 product for all its variants (In one query):
SELECT
p.productID,
p.name,
p.longDescription,
p.shortDescription,
colors.color,
sizes.size,
pvar.quantity,
pprice.price,
images.imageFilename
FROM products as p
JOIN product_variants as pvar
ON p.productID = pvar.productID
JOIN productpricing as pprice
ON pprice.productID = p.productID
JOIN colors
ON colors.colorID = pvar.colorID
JOIN sizes
ON sizes.sizeID = pvar.sizeID
JOIN product_variant_images as pvari
ON pvari.productvariantID = pvar.productvariantID
JOIN images
ON images.imageID = pvari.imageID
WHERE p.productID = 1 AND pvar.isActive = 1 AND NOW() BETWEEN pprice.startDate AND pprice.endDate;
Example return set:
# productID, name, longDescription, shortDescription, color, size, quantity, price, imageFilename
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'White', '50', '2', '309.99', 'shirtwhite1.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'White', '50', '2', '309.99', 'shirtwhite2.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'White', '56', '5', '309.99', 'shirtwhite1.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'White', '56', '5', '309.99', 'shirtwhite2.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'Blue', '56', '1', '309.99', 'shirtblue1.jpg'
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', 'Blue', '56', '1', '309.99', 'shirtblue2.jpg'
As can be seen in the result set the white variant (of one size) of the shirt is associated with two images, therefore there are two records with only imageFilename containing "new data".
Example of using several queries:
SELECT
p.productID,
p.name,
p.longDescription,
p.shortDescription,
pprice.price
FROM products as p
JOIN productpricing as pprice
ON pprice.productID = p.productID
WHERE p.productID = 1 AND NOW() BETWEEN pprice.startDate AND pprice.endDate;
SELECT
c.color,
s.size,
pvar.quantity
FROM product_variants as pvar
JOIN colors AS c
ON c.colorID = pvar.colorID
JOIN sizes as s
ON s.sizeID = pvar.sizeID
WHERE pvar.productID = 1;
SELECT DISTINCT
c.color,
i.imageFilename
FROM images AS i
JOIN product_variant_images as pvari
ON pvari.imageID = i.imageID
JOIN product_variants as pvar
ON pvar.productvariantID = pvari.productvariantID
JOIN colors AS c
ON c.colorID = pvar.colorID
WHERE pvar.productID = 1;
Several queries result sets:
# productID, name, longDescription, shortDescription, price
'1', 'A shirt', 'Long description of this product', 'shortDesc of shirt', '309.99'
# color, size, quantity
'White', '50', '2'
'Blue', '56', '1'
'White', '56', '5'
# color, imageFilename
'White', 'shirtwhite1.jpg'
'White', 'shirtwhite2.jpg'
'Blue', 'shirtblue1.jpg'
'Blue', 'shirtblue2.jpg'
Any advice regarding the database design/queries is much appreciated!
I have dumped a sql table for cities, regions & countries.
I have the .sql files in my local machine. I need to convert them to CSV format.
So I have a 2 part question.
1): What is the best way to do this? Is any tools I can use? (I have a Mac)
2):
I found this site. So I tried with this code:
CREATE TABLE IF NOT EXISTS `countries` (
`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`code` varchar(10) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=231 DEFAULT CHARSET=latin1;
-- Dumping data for table dddblog.countries: ~218 rows (approximately)
/*!40000 ALTER TABLE `countries` DISABLE KEYS */;
INSERT INTO `countries` (`id`, `name`, `code`) VALUES
(1, 'Andorra', 'ad'),
(2, 'United Arab Emirates', 'ae'),
(3, 'Afghanistan', 'af'),
(4, 'Antigua and Barbuda', 'ag'),
(5, 'Anguilla', 'ai'),
(6, 'Albania', 'al'),
(7, 'Armenia', 'am'),
(8, 'Netherlands Antilles', 'an'),
(9, 'Angola', 'ao'),
// Other countries
/*!40000 ALTER TABLE `countries` ENABLE KEYS */;
And when I click on Convert, I get error: Missing SELECT STATEMENT.
Im not familiar with SQL and any help is appreciated!
The message Missing SELECT STATEMENT says it all: You have to add a SELECT statement.
Try it with this code - worked for me:
CREATE TABLE IF NOT EXISTS `countries` (
`id` smallint(5) NOT NULL ,
`name` varchar(255) NOT NULL,
`code` varchar(10) NOT NULL
);
INSERT INTO `countries` (`id`, `name`, `code`) VALUES
(1, 'Andorra', 'ad'),
(2, 'United Arab Emirates', 'ae'),
(3, 'Afghanistan', 'af'),
(4, 'Antigua and Barbuda', 'ag'),
(5, 'Anguilla', 'ai'),
(6, 'Albania', 'al'),
(7, 'Armenia', 'am'),
(8, 'Netherlands Antilles', 'an'),
(9, 'Angola', 'ao')
;
SELECT `id`, `name`, `code` FROM `countries`
I have two tables named as
product_Category
CREATE TABLE `product_category` (
`pid` int(11) NOT NULL,
`cid` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `product_category`
--
/*!40000 ALTER TABLE `product_category` DISABLE KEYS */;
INSERT INTO `product_category` (`pid`,`cid`) VALUES
(1,1),
(2,3),
(3,2),
(4,2),
(5,3),
(1,2),
(2,4),
(3,1);
and category table
CREATE TABLE `category` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cat_name` varchar(50) NOT NULL,
`mapped_cat_id` varchar(250) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
--
-- Dumping data for table `category`
--
/*!40000 ALTER TABLE `category` DISABLE KEYS */;
INSERT INTO `category` (`id`,`cat_name`,`mapped_cat_id`) VALUES
(1,'c1','1,2,4'),
(2,'c2','2,3'),
(3,'c3','3,4'),
(4,'c4','4,1,3');
/*!40000 ALTER TABLE `category` ENABLE KEYS */;
When I run this query
select distinct pid from
product_category where cid in (1,2,4)
I got result of pid (1,3,4,2)
but when I run query
select distinct pid from
product_category where cid in (select mapped_cat_id from category where id=1)
I got result of pid (1,3)
How to use subquery with 'IN' clause ?
I know my way of asking question is wrong because I dont know how to create table here thats why I wrote query instead of table.
i think coma separated values are not good.
Delete contents of your category table and use following query to insert
INSERT INTO `category` (`id`, `cat_name`, `mapped_cat_id`) VALUES
(1, 'c1', '1'),
(2, 'c2', '2'),
(3, 'c3', '3'),
(4, 'c4', '4'),
(5, 'c1', '2'),
(6, 'c1', '4'),
(7, 'c2', '3'),
(8, 'c3', '4'),
(9, 'c4', '1'),
(10, 'c4', '3');
Then use following query to get your result
select distinct pid from
product_category where cid in
(select mapped_cat_id from category where cat_name='c1')
You might be looking for FIND_IN_SET() function
select distinct pid from
product_category where FIND_IN_SET(cid,
(select mapped_cat_id from category where id=1));
SAMPLE FIDDLE
Since mapped_cat_id is saved as comma - seperated values(varchar), it is taking only first integer for the condition checking. Here it is 1