accelerate very large mysql join - mysql

Problem:
Query aborts after 10 minutes
Query should be faster
I created the following query. This one was the fastest of several versions. Unfortunately, with more data, even this one abots after 600 seconds with an error "Error Code: 2013. Lost connection to MySQL server during query".
CREATE OR REPLACE VIEW 1 AS
SELECT `Timeperiod` AS `Timeperiod` ,
"at" AS `Domain` ,
`Content Groups`AS `Content Groups`,
...
FROM a
UNION ALL
SELECT `Timeperiod` AS `Timeperiod` ,
"com" AS `Domain` ,
`Content Groups`AS `Content Groups`,
...
FROM b
UNION ALL
SELECT `Timeperiod` AS `Timeperiod` ,
"com" AS `Domain`,
`Content Groups`AS `Content Groups`,
...
FROM c
UNION ALL
SELECT `Timeperiod` AS `Timeperiod` ,
"fr" AS `Domain`,
`Content Groups`AS `Content Groups`,
...
FROM d
UNION ALL
SELECT `Timeperiod` AS `Timeperiod` ,
"it" AS `Domain`,
`Content Groups`AS `Content Groups`,
...
FROM e;
CREATE OR REPLACE VIEW 2 AS
SELECT `Timeperiod` AS `Timeperiod` ,
`Content Group` AS `Content Group` ,
"at" AS `Domain`,
...
FROM f
UNION ALL
SELECT `Timeperiod` AS `Timeperiod` ,
`Content Group` AS `Content Group` ,
"com" AS `Domain`,
...
FROM g
UNION ALL
SELECT `Timeperiod` AS `Timeperiod` ,
`Content Group` AS `Content Group` ,
"com" AS `Domain`,
...
FROM h
UNION ALL
SELECT `Timeperiod` AS `Timeperiod` ,
`Content Group` AS `Content Group` ,
"fr" AS `Domain`,
...
FROM i
UNION ALL
SELECT `Timeperiod` AS `Timeperiod` ,
`Content Group` AS `Content Group` ,
"it" AS `Domain`,
...
FROM j;
CREATE OR REPLACE VIEW 3 AS
SELECT CG.`Domain` AS `Domain` ,
TP.`TimeperiodAlias` AS `Timeperiod` ,
CG.`Content Groups` AS `Content Group` ,
M.`InternalName` AS `Internal Model Name`,
...
FROM 1 CG ,
Timperiods TP ,
Models M
WHERE CG.`Content Groups` LIKE CONCAT(M.`ContentGroupName`, '%')
AND CG.`Timeperiod` = TP.`Timeperiod`;
CREATE OR REPLACE VIEW 4 AS
SELECT CGD.`Domain` AS `Domain` ,
TP.`TimeperiodAlias` AS `Timeperiod` ,
CGD.`Content Group` AS `Content Group`,
...
FROM 2 CGD,
Timeperiods TP ,
Models M
WHERE CGD.`Content Group` LIKE CONCAT(M.`ContentGroupName`, '%')
AND CGD.`Timeperiod` = TP.`Timeperiod`;
DROP TABLE IF EXISTS 5;
CREATE TABLE IF NOT EXISTS 5
(
`Domain` VARCHAR(3) NOT NULL ,
`Timeperiod` VARCHAR(30) NOT NULL,
`Content Group` varchar(70),
`Internal Model Name` VARCHAR(50),
...
PRIMARY KEY (`Domain`,`Timeperiod`, `Content Group`)
)
AS
SELECT CG.`Domain` AS `Domain` ,
CG.`Timeperiod` AS `Timeperiod` ,
CG.`Content Group` AS `Content Group` ,
CG.`Internal Model Name` AS `Internal Model Name`,
...
FROM 3 CG,
4 CGD
WHERE CG.`Content Group` = CGD.`Content Group`
AND CG.`Timeperiod` = CGD.`Timeperiod`
AND CG.`Domain` = CGD.`Domain`;
These are the number of rows of the steps:
1: 64763
2: 51932
Timeperiods: 36
Models: 15
3: 2706
4: 2172
This is the EXPLAIN:
'1', 'PRIMARY', 'M', 'ALL', NULL, NULL, NULL, NULL, '15', ''
'1', 'PRIMARY', 'M', 'index', NULL, 'CGIndex', '242', NULL, '15', 'Using index; Using join buffer'
'1', 'PRIMARY', '<derived3>', 'ALL', NULL, NULL, NULL, NULL, '9528', 'Using where; Using join buffer'
'1', 'PRIMARY', 'TP', 'eq_ref', 'PRIMARY', 'PRIMARY', '65', 'CG.Timeperiod', '1', ''
'1', 'PRIMARY', '<derived9>', 'ALL', NULL, NULL, NULL, NULL, '21226', 'Using where; Using join buffer'
'1', 'PRIMARY', 'TP', 'eq_ref', 'PRIMARY', 'PRIMARY', '65', 'CGD.Timeperiod', '1', 'Using where'
'9', 'DERIVED', 'ContentGroupDuration_jMKL35_ALL', 'ALL', NULL, NULL, NULL, NULL, '17794', ''
'10', 'UNION', 'ContentGroupDurationVisitDuration_k4cZ5M_ALL', 'ALL', NULL, NULL, NULL, NULL, '1', ''
'11', 'UNION', 'ContentGroupDurationVisitDuration_k4cZ5M_ALL', 'ALL', NULL, NULL, NULL, NULL, '1', ''
'12', 'UNION', 'ContentGroupDuration_jMKL35_ALL', 'ALL', NULL, NULL, NULL, NULL, '1', ''
'13', 'UNION', 'ContentGroupDuration_jMKL35_ALL', 'ALL', NULL, NULL, NULL, NULL, '1', ''
NULL, 'UNION RESULT', '<union9,10,11,12,13>', 'ALL', NULL, NULL, NULL, NULL, NULL, ''
'3', 'DERIVED', 'ContentGroups_fd33ef1_ALL', 'ALL', NULL, NULL, NULL, NULL, '1', ''
'4', 'UNION', 'ContentGroups_fd33ef1_ALL', 'ALL', NULL, NULL, NULL, NULL, '1', ''
'5', 'UNION', 'ContentGroups_fd33ef1_ALL', 'ALL', NULL, NULL, NULL, NULL, '1', ''
'6', 'UNION', 'ContentGroups_fd33ef1_ALL', 'ALL', NULL, NULL, NULL, NULL, '10476', ''
'7', 'UNION', 'ContentGroups_fd33ef1_ALL', 'ALL', NULL, NULL, NULL, NULL, '1', ''
NULL, 'UNION RESULT', '<union3,4,5,6,7>', 'ALL', NULL, NULL, NULL, NULL, NULL, ''
Does anyone know a way how to fasten the query and/or how to avoid the connection abort?
Solution:
Problem 1: execute "set wait_timeout=2147483" from command line (not inside sql)
Problem 2: store intermediate results in temporary tables and add indexes. Then perform the large join.
Best
Christian

I'd say there's two ways :
- either change the timeout on non interactive connections (wait_timeout for Mysql)
- or optimize somehow your table structure
I've been working on large commercial databases in the past, the performance of you joins relates more to the way your tables are indexes than to the amount of lines fetched. Make sure the right tables have the right keys, try to augment those if possible.
Anyway, change the wait_timeout, a connection shouldnt die so soon if you allow for long and complex queries.
To change the timeout, log in as root to mysql :
mysql -u root -p, input password, and enter the following :
set global wait_timeout=2147483
thats the max value corresponding to 23 days on windows. It can get much higher on Linux distribs, but well you don't need that long anyway.
cheers,

Related

How can I compare an array column with JSON Object column and return the maximum discount in PHP MYSQL

As requested by #Adyson, I've added my database sample below.
CREATE TABLE test.patients ( id BIGINT NOT NULL AUTO_INCREMENT , branchID INT NOT NULL , patientNo VARCHAR(20) NOT NULL , billingAccounts VARCHAR(50) NOT NULL , firstName VARCHAR(20) NOT NULL , lastName VARCHAR(20) NOT NULL , PRIMARY KEY (id)) ENGINE = InnoDB;
INSERT INTO `patients` (`id`, `branchID`, `patientNo`, `billingAccounts`, `firstName`, `lastName`) VALUES (NULL, '1', 'PN017830', '[\"-1\",\"-2\",\"7632\",\"7774\"]', 'John', 'Daka'), (NULL, '1', 'PN017890', '[\"-1\",\"-2\",\"8120\",\"7742\"]', 'Ann', 'Mikail')
CREATE TABLE `test`.`products` ( `id` INT NOT NULL AUTO_INCREMENT , `type` ENUM('pharmaceutical','other') NOT NULL , `productCode` VARCHAR(50) NOT NULL , `brand` VARCHAR(50) NOT NULL , `manufacturer` INT NOT NULL , `generics` TEXT NOT NULL , `privateBranchID` INT NOT NULL , `regID` INT NOT NULL , `regDate` DATETIME NOT NULL , `status` ENUM('active','deactivated') NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;
INSERT INTO `products` (`id`, `type`, `productCode`, `brand`, `manufacturer`, `generics`, `privateBranchID`, `regBy`, `regDate`, `status`) VALUES (10, 'pharmaceutical', '500015806877', 'gaviscon', '217', '[\"magaldrate\",\"simethicone\"]', '0', '1', CURRENT_TIMESTAMP, 'active'), (11, 'pharmaceutical', '7640153080325', 'lofral', '199', '[\"amlodipine\"]', '0', '1', CURRENT_TIMESTAMP, 'active')
CREATE TABLE `test`.productdiscounts ( `id` BIGINT NOT NULL AUTO_INCREMENT , `branchID` INT NOT NULL , `productID` BIGINT NOT NULL , `accountID` BIGINT NOT NULL , `discount` DECIMAL NOT NULL , `isActive` ENUM('1','0') NOT NULL , `regBy` INT NOT NULL , `regTimestamp` DATETIME on update CURRENT_TIMESTAMP NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;
INSERT INTO `productdiscounts` (`branchID`, `productID`, `accountID`, `discount`, `isActive`, `regBy`, `regTimestamp`) VALUES ('1', '10', '7723', '90', '1', '1', '2022-08-25 08:01:59'), ('1', '10', '7724', '70', '1', '1', '2022-08-25 08:01:59'), ('1', '10', '-2', '70', '1', '1', '2022-08-25 08:01:59'), ('1', '10', '7720', '55', '1', '1', '2022-08-25 08:01:59')
CREATE TABLE chms.patients ( id BIGINT NOT NULL AUTO_INCREMENT , branchID INT NOT NULL , patientNo VARCHAR(20) NOT NULL , billingAccounts VARCHAR(50) NOT NULL , firstName VARCHAR(20) NOT NULL , lastName VARCHAR(20) NOT NULL , PRIMARY KEY (id)) ENGINE = InnoDB;
INSERT INTO patients (id, branchID, patientNo, billingAccounts, firstName, lastName) VALUES (NULL, '1', 'PN017830', '["-1","-2","7632","7774"]', 'John', 'Daka'), (NULL, '1', 'PN017890', '["-1","-2","8120","7742"]', 'Ann', 'Mikail')
CREATE TABLE test.products ( id INT NOT NULL AUTO_INCREMENT , type ENUM('pharmaceutical','other') NOT NULL , productCode VARCHAR(50) NOT NULL , brand VARCHAR(50) NOT NULL , manufacturer INT NOT NULL , generics TEXT NOT NULL , privateBranchID INT NOT NULL , regID INT NOT NULL , regDate DATETIME NOT NULL , status ENUM('active','deactivated') NOT NULL , PRIMARY KEY (id)) ENGINE = InnoDB;
INSERT INTO products (id, type, productCode, brand, manufacturer, generics, privateBranchID, regBy, regDate, status) VALUES (10, 'pharmaceutical', '500015806877', 'gaviscon', '217', '["magaldrate","simethicone"]', '0', '1', CURRENT_TIMESTAMP, 'active'), (11, 'pharmaceutical', '7640153080325', 'lofral', '199', '["amlodipine"]', '0', '1', CURRENT_TIMESTAMP, 'active')
CREATE TABLE test.productdiscounts ( id BIGINT NOT NULL AUTO_INCREMENT , branchID INT NOT NULL , productID BIGINT NOT NULL , accountID BIGINT NOT NULL , discount DECIMAL NOT NULL , isActive ENUM('1','0') NOT NULL , regBy INT NOT NULL , regTimestamp DATETIME on update CURRENT_TIMESTAMP NOT NULL , PRIMARY KEY (id)) ENGINE = InnoDB;
INSERT INTO productdiscounts (branchID, productID, accountID, discount, isActive, regBy, regTimestamp) VALUES ('1', '10', '7723', '90', '1', '1', '2022-08-25 08:01:59'), ('1', '10', '7724', '70', '1', '1', '2022-08-25 08:01:59'), ('1', '10', '-2', '70', '1', '1', '2022-08-25 08:01:59'), ('1', '10', '7720', '55', '1', '1', '2022-08-25 08:01:59')
This is my PHP code:
$searchSQL="
select
distinct
products.id,
products.type as productType,
products.brand,
products.status,
products.productCode,
products.generics,
coalesce((select discount from productdiscounts where(productID=products.id and branchID=1 and isActive='1' and patients.billingAccounts like concat('%\"',productdiscounts.accountID,'\"%')) order by discount desc limit 1),0.00) as discount,
concat('[',(select group_concat('{\"productID\":\"',productdiscounts.productID,'\",\"accountID\":\"',productdiscounts.accountID,'\",\"discount\":\"',productdiscounts.discount,'\",\"accountName\":\"',accounts.name,'\",\"accountNo\":\"',accounts.accountNo,'\"}') from productdiscounts
left join accounts on(productdiscounts.accountID=accounts.id)
where(productdiscounts.productID=products.id and productdiscounts.branchID = 1 and productdiscounts.isActive = '1' )),']') as allDiscounts
from products
left join stock on (products.id=stock.productID)
left join pricetags on (stock.priceTag=pricetags.id)
left join countries on (products.manufacturer=countries.id)
left join diagnosis on (diagnosis.diagnosisRef='')
left join patients on (patients.id=10)
where(
products.productCode='' or
products.generics like concat('%\"','magaldrate','%\"%') or
products.brand like concat('% ','gaviscon','%')
and (stock.isActive='1' and products.status='active')
)
order by products.brand limit 30
";
$productsQ=(new DB)->getRef()->prepare($searchSQL);
$productsQ->execute([]]);
$productsD=$productsQ->fetchAll();
I have a column (billingAccounts) that stores data as JSON Array like this: ["-1","-2","7632","7774"]
and in the product-discount table, I have rows for each company's discount and to get the discount, I use the below code but is a bit slow:
[enter image description here][1]
select products.brand, coalesce((select discount from productdiscounts where(productID=products.id and branchID=? and isActive='1' and patients.billingAccounts like concat('%"',productdiscounts.accountID,'"%')) order by discount desc limit 1),0.00) as discount
from products
left join patients on (patients.patientNo=diagnosis.patientNo)
.....
ALL I WANT IS TO RETURN THE MAXIMUM DISCOUNT FROM THE company ACCOUNTS THAT HAVE DISCOUNT ON THE PRODUCT AND ALSO MATCHES IN THE PATIENT BILLING ACCOUNS.
Note: I don't want to use concat or group_concat because is what I'm currently using and it's making the query to be slow!

Mysql self joining on max id returned from first table using groupby

I need a sql that uses self join on max id from first table. Please look at the following image of table. I am grouping the table by service_id but I need the last message of each group. So For service group 5 message count should be 3 and last_message should be thirdMsg5. I wrote a sql below everything else is fine but it is throwing an error in the case of self join. It can't recognize msgTbl1.last_message_id. I think I am calling it before preparing it. I need help to solve this problem what would be the best sql to solve this in one query? And if possible please provide me this query in laravel query builder format.
SELECT count(msgTbl1.id) as message_count,
max(msgTbl1.id) as last_message_id,
msgTbl1.body,
msgTbl2.body as last_message,
services.name as service_name
FROM messages msgTbl1
LEFT JOIN (SELECT id, body FROM messages) AS msgTbl2
ON msgTbl2.id = msgTbl1.last_message_id
LEFT JOIN services on services.id = msgTbl1.service_id
WHERE receiver_id = 4 AND read_user = 'no'
GROUP BY msgTbl1.service_id
sql for the message table
CREATE TABLE `messages` (
`id` int(11) UNSIGNED NOT NULL,
`sender_id` int(11) UNSIGNED DEFAULT NULL,
`receiver_id` int(11) UNSIGNED DEFAULT NULL,
`service_id` int(11) UNSIGNED NOT NULL,
`sender_type` enum('user','agent','admin') NOT NULL,
`receiver_type` enum('user','agent','admin') NOT NULL,
`body` text,
`files` varchar(500) DEFAULT NULL COMMENT 'serialize',
`new_notification` enum('no','yes') NOT NULL DEFAULT 'yes',
`read_user` enum('yes','no') NOT NULL DEFAULT 'no',
`read_agent` enum('yes','no') NOT NULL DEFAULT 'no',
`status` enum('active','archive','deleted') NOT NULL DEFAULT 'active',
`created_at` datetime NOT NULL,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `messages` (`id`, `sender_id`, `receiver_id`, `service_id`, `sender_type`, `receiver_type`, `body`, `files`, `new_notification`, `read_user`, `read_agent`, `status`, `created_at`, `updated_at`) VALUES
(1, 22, 4, 5, 'user', 'agent', 'firstMsg5', NULL, 'yes', 'no', 'yes', 'active', '2016-03-24 00:00:00', '2016-04-12 05:40:28'),
(2, 22, 4, 5, 'user', 'agent', 'secondMsg5', NULL, 'yes', 'no', 'yes', 'active', '2016-03-24 00:00:00', '2016-04-12 05:40:31'),
(3, 22, 4, 9, 'user', 'agent', 'firstMsg9', NULL, 'yes', 'yes', 'yes', 'active', '2016-03-24 00:00:00', '2016-04-12 05:40:45'),
(4, 4, 4, 9, 'agent', 'user', 'secondMsg9', NULL, 'yes', 'yes', 'yes', 'active', '2016-03-24 00:00:00', '2016-04-12 05:40:56'),
(5, 22, 4, 5, 'user', 'agent', 'thirdMsg5', NULL, 'yes', 'yes', 'yes', 'active', '2016-03-24 00:00:00', '2016-04-12 05:41:08');
Try this:
SELECT message_count,
last_message_id,
msgTbl1.body,
services.name as service_name
FROM messages msgTbl1
INNER JOIN (
SELECT MAX(id) AS last_message_id, COUNT(*) AS message_count
FROM messages
WHERE read_user = 'no'
GROUP BY service_id) AS msgTbl2
ON msgTbl1.id = msgTbl2.last_message_id
LEFT JOIN services on services.id = msgTbl1.service_id
WHERE receiver_id = 4

What is the best optimization for this table and its queries?

I have this table:
CREATE TABLE IF NOT EXISTS `listings` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`type` tinyint(1) NOT NULL DEFAULT '1',
`hash` char(32) NOT NULL,
`source_id` int(10) unsigned NOT NULL,
`link` varchar(255) NOT NULL,
`short_link` varchar(255) NOT NULL,
`cat_id` mediumint(5) NOT NULL,
`title` mediumtext NOT NULL,
`description` mediumtext,
`content` mediumtext,
`images` mediumtext,
`videos` mediumtext,
`views` int(10) NOT NULL,
`comments` int(11) NOT NULL DEFAULT '0',
`comments_update` int(11) NOT NULL DEFAULT '0',
`editor_id` int(11) NOT NULL DEFAULT '0',
`auther_name` varchar(255) DEFAULT NULL,
`createdby_id` int(10) NOT NULL,
`createdon` int(20) NOT NULL,
`editedby_id` int(10) NOT NULL,
`editedon` int(20) NOT NULL,
`deleted` tinyint(1) NOT NULL,
`deletedon` int(20) NOT NULL,
`deletedby_id` int(10) NOT NULL,
`deletedfor` varchar(255) NOT NULL,
`published` tinyint(1) NOT NULL DEFAULT '1',
`publishedon` int(20) NOT NULL,
`publishedby_id` int(10) NOT NULL,
PRIMARY KEY (`id`),
KEY `hash` (`hash`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT AUTO_INCREMENT=91628 ;
and some bad queries like this
SELECT id,type , source_id, link, short_link, cat_id, title,
description, images, views, comments, published, publishedon,
content, comments_update, editor_id, auther_name, createdby_id,
createdon, editedby_id, editedon, deleted, deletedon,
deletedby_id, deletedfor, publishedby_id
FROM listings
WHERE (cat_id IN ( 98 ))
AND (TYPE IN ('1'))
AND (source_id IN ('78'))AND (FROM_UNIXTIME( publishedon ) >= SUBDATE( NOW( ) ,
1 )
)
AND (deleted =0)
AND (published =1)
ORDER BY `publishedon` DESC
LIMIT 10 OFFSET 0
and
SELECT id,type,source_id,link,short_link,cat_id,title,description,
images,views,comments,published,publishedon
FROM listings
WHERE (title RLIKE 'اليمن|عدن')
AND (id != 89024)
AND (deleted = 0)
AND (published = 1)
ORDER BY publishedon DESC
LIMIT 6 OFFSET 0
and
SELECT MIN(id) FROM listings
WHERE (id > 91152) AND (cat_id = '134')
and
SELECT COUNT(id)
FROM listings
WHERE (publishedon >= '1442963362'
AND publishedon <= '1443568162'
)
AND (cat_id IN ('19', '20', '21', '22', '23', '24', '27',
'32', '35', '110', '54', '38', '39', '41', '42', '43',
'44', '45', '46', '47', '49', '56', '57', '51', '55',
'58', '59', '60', '61', '62', '102', '95', '96', '98',
'101', '103', '104', '105', '106', '124', '125', '130',
'131', '132', '133', '134', '135')
)
this query may takes 0.4 sec to be done. Any in one page it may contain 5 queries like this. This is very big problem; it causes server load and down time.
This query
SELECT *
FROM `listings`
WHERE id = 5455
takes 0.0004 sec to be done because its depend on the index of pk
How can I make indexes for columns in the first query?
And many times when I use "show processlist", I see this "Waiting for table level lock" too much and "sorting data".
The application insert/update many rows all the times; how can I solve this?
Your query is basically:
SELECT . . .
FROM listings l
WHERE cat_id = 98 AND
TYPE = 1 AND
source_id = 78 AND
deleted = 0 AND
published = 1 AND
FROM_UNIXTIME( publishedon ) >= SUBDATE( NOW( ) , 1 )
ORDER BY `publishedon` DESC
LIMIT 10
OFFSET 0
For performance, start with a composite index on (cat_id, type, source_id, deleted, published, publishedon). The syntax is:
create index idx_listings_6 on listings(cat_id, type, source_id, deleted, published, publishedon desc);
Next, I would suggest re-writing the where clause as:
SELECT . . .
FROM listings l
WHERE cat_id = 98 AND
TYPE = 1 AND
source_id = 78 AND
deleted = 0 AND
published = 1 AND
publishedon >= UNIX_TIMESTAMP(SUBDATE( NOW( ) , 1 ))
ORDER BY `publishedon` DESC
LIMIT 10
OFFSET 0
And use the same index above.
MySQL cannot declare an index to be DESC; it ignores that keyword and builds an ASC index. However, ORDER BY x DESC is still optimizable.
MySQL will use only one INDEX per SELECT. (Usually).
The optimal index starts with any WHERE clause fields with '='.
Query 1: Already discussed.
Query 2: INDEX(deleted, published, publishedon)
Query 3: INDEX(cat_id, id)
Query 4: Change COUNT(id) to COUNT(*) and add either INDEX(cat_id, publishedon) and/or INDEX(publishedon, cat_id). It is not obvious which index would be better. Add both and let the optimizer decide. As in your other question, it may be beneficial to PARTITION BY RANGE(publishedon) and use INDEX(cat_id, publishedon) (not the other one).
Consider switching to InnoDB.

MySQL - Return columns based on value

I need to retrieve data from a database and I have no way to change its structure.
There are 3 distinct fields for address:
Personal: client_address_1, client_address_2, client_address_3, client_address_4
Postal: client_postaladdress_1, client_postaladdress_2, client_postaladdress_3, client_postaladdress_4
Company: client_company_address_1, client_company_address_2, client_company_address_3, client_company_address_4
and one field (client_prefered_address) which contains in which address the client wants to receive his correspondence.
From them I need to retrieve the address of choice, so, if the client is marked as postal, it should return the columns: client_postaladdress_1, client_postaladdress_2, client_postaladdress_3, client_postaladdress_4 but not the others.
Is there any way to do it? I have been Googling for two days.
Thanks
SQL:
CREATE TABLE IF NOT EXISTS `client` (
`client_id` int(11) NOT NULL AUTO_INCREMENT,
`client_address_1` varchar(255) COLLATE utf8_bin NOT NULL,
`client_address_2` varchar(255) COLLATE utf8_bin NOT NULL,
`client_address_3` varchar(255) COLLATE utf8_bin NOT NULL,
`client_address_4` varchar(255) COLLATE utf8_bin NOT NULL,
`client_postaladdress_1` varchar(255) COLLATE utf8_bin NOT NULL,
`client_postaladdress_2` varchar(255) COLLATE utf8_bin NOT NULL,
`client_postaladdress_3` varchar(255) COLLATE utf8_bin NOT NULL,
`client_postaladdress_4` varchar(255) COLLATE utf8_bin NOT NULL,
`client_company_address_1` varchar(255) COLLATE utf8_bin NOT NULL,
`client_company_address_2` varchar(255) COLLATE utf8_bin NOT NULL,
`client_company_address_3` varchar(255) COLLATE utf8_bin NOT NULL,
`client_company_address_4` varchar(255) COLLATE utf8_bin NOT NULL,
`client_prefered_address` char(10) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=4;
--
-- Dumping data for table `client`
--
INSERT INTO `client` (`client_id`, `client_address_1`, `client_address_2`, `client_address_3`, `client_address_4`, `client_postaladdress_1`, `client_postaladdress_2`, `client_postaladdress_3`, `client_postaladdress_4`, `client_company_address_1`, `client_company_address_2`, `client_company_address_3`, `client_company_address_4`, `client_prefered_address`) VALUES
(1, 'Yellow house', 'Yellow street, 25', '09090 Yellow city', 'Yellow Country', 'Blue postbox', 'Blue avenue, 90', '09039 Blue city', 'Blue Country', 'Green house', 'Green street, 100', '02930 Green city', 'Green Country', 'Postal'),
(2, 'Apple house', 'Apple street, 200', 'Apple State 2039', 'Apple Land', 'Melon House', 'Melon Boulevard ', 'Melon State ', 'Melon Land', '', '', '', '', 'Personal'),
(3, '', '', '', '', '', '', '', '', 'Chocolate Factory', 'Charlie street 293', 'Chocolate CH', 'Chocolate Kingdom ', 'Company');
Try this with CASE
select
(CASE WHEN client_prefered_address ='Postal'
THEN client_postaladdress_1
WHEN client_prefered_address ='Personal' THEN client_address_1
WHEN client_prefered_address ='Company' THEN client_company_address_1
ELSE NULL END) `addressone`
,
(CASE WHEN client_prefered_address ='Postal'
THEN client_postaladdress_2
WHEN client_prefered_address ='Personal' THEN client_address_2
WHEN client_prefered_address ='Company' THEN client_company_address_2
ELSE NULL END) `addresstwo`,
(CASE WHEN client_prefered_address ='Postal'
THEN client_postaladdress_3
WHEN client_prefered_address ='Personal' THEN client_address_3
WHEN client_prefered_address ='Company' THEN client_company_address_3
ELSE NULL END) `addressthree`,
(CASE WHEN client_prefered_address ='Postal'
THEN client_postaladdress_4
WHEN client_prefered_address ='Personal' THEN client_address_4
WHEN client_prefered_address ='Company' THEN client_company_address_4
ELSE NULL END) `addressfour`
FROM `client`
FIDDLE
I suggest using 2 queries to solve this. One to get the preferred address, and another to get the correct fields. Use whatever programming language you are using to generte the 2nd query with the right fields.
If you really want to use MySQL, you can use the CASE statement (it's like a switch):
SELECT client_id, (
CASE client_prefered_address
WHEN 'Postal' THEN client_postaladdress_1
WHEN 'Personal' THEN client_address_1
WHEN 'Company' THEN client_company_address_1
END) AS address_1, (
CASE client_prefered_address
WHEN 'Postal' THEN client_postaladdress_2
WHEN 'Personal' THEN client_address_2
WHEN 'Company' THEN client_company_address_2
END) AS address_2, (
CASE client_prefered_address
WHEN 'Postal' THEN client_postaladdress_3
WHEN 'Personal' THEN client_address_3
WHEN 'Company' THEN client_company_address_3
END) AS address_3, (
CASE client_prefered_address
WHEN 'Postal' THEN client_postaladdress_4
WHEN 'Personal' THEN client_address_4
WHEN 'Company' THEN client_company_address_4
END) AS address_4
FROM client
DEMO: http://sqlfiddle.com/#!2/eab6b/3
Try something like this:
SELECT `client_id`,
IF(`client_prefered_address` = 'personal', `client_address_1`, IF(`client_prefered_address` = 'postal', `client_postaladdress_1`, `client_company_address_1`) AS `address_1`,
IF(`client_prefered_address` = 'personal', `client_address_2`, IF(`client_prefered_address` = 'postal', `client_postaladdress_2`, `client_company_address_2`) AS `address_2`,
IF(`client_prefered_address` = 'personal', `client_address_3`, IF(`client_prefered_address` = 'postal', `client_postaladdress_3`, `client_company_address_3`) AS `address_3`,
IF(`client_prefered_address` = 'personal', `client_address_4`, IF(`client_prefered_address` = 'postal', `client_postaladdress_4`, `client_company_address_4`) AS `address_4`
FROM `client`
WHERE blahblahblah

database query to find trains between two stations on specific date like irctc.co.in

I have following database
Table structure for table trains
CREATE TABLE IF NOT EXISTS `trains` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`train_no` varchar(5) COLLATE latin1_general_ci DEFAULT NULL,
`train_name` varchar(50) COLLATE latin1_general_ci NOT NULL,
`runsfrom` varchar(50) COLLATE latin1_general_ci NOT NULL,
`SUN` varchar(3) COLLATE latin1_general_ci NOT NULL,
`MON` varchar(3) COLLATE latin1_general_ci NOT NULL,
`TUE` varchar(3) COLLATE latin1_general_ci NOT NULL,
`WED` varchar(3) COLLATE latin1_general_ci NOT NULL,
`THU` varchar(3) COLLATE latin1_general_ci NOT NULL,
`FRI` varchar(3) COLLATE latin1_general_ci NOT NULL,
`SAT` varchar(3) COLLATE latin1_general_ci NOT NULL,
`DOE` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=1912 ;
having data like this:
INSERT INTO `trains` VALUES (269, '12307', 'HWH JU EXPRESS', 'HOWRAH JN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN', '2013-03-24');
INSERT INTO `trains` VALUES (270, '12308', 'JU HWH SUPFAST', 'JODHPUR JN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN', '2013-03-24');
INSERT INTO `trains` VALUES (381, '12461', 'MANDOR EXPRESS', 'DELHI', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN', '2013-03-24');
INSERT INTO `trains` VALUES (382, '12462', 'MANDOR EXPRESS', 'JODHPUR JN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN', '2013-03-24');
Table structure for table train_number
CREATE TABLE IF NOT EXISTS `train_number` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`train_no` varchar(5) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1912 ;
having data like this:
INSERT INTO `train_number` VALUES (269, '12307');
INSERT INTO `train_number` VALUES (270, '12308');
INSERT INTO `train_number` VALUES (381, '12461');
Table structure for table train_schedule
CREATE TABLE IF NOT EXISTS `train_schedule` (
`train_no` varchar(5) NOT NULL,
`stn_code` varchar(20) NOT NULL,
`stn_name` varchar(50) NOT NULL,
`route_no` varchar(2) NOT NULL,
`arr_time` varchar(5) NOT NULL,
`dep_time` varchar(5) NOT NULL,
`halt_time` varchar(5) NOT NULL,
`distance` varchar(4) NOT NULL,
`day` varchar(2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
having data like this:
INSERT INTO `train_schedule` VALUES ('12307', 'HWH ', 'HOWRAH JN ', '1', 'Sourc', '23:30', '', '0', '1');
INSERT INTO `train_schedule` VALUES ('12307', 'BWN ', 'BARDDHAMAN JN ', '1', '00:35', '00:37', '2:00', '95', '2');
INSERT INTO `train_schedule` VALUES ('12307', 'ASN ', 'ASANSOL JN ', '1', '01:52', '01:56', '4:00', '200', '2');
INSERT INTO `train_schedule` VALUES ('12307', 'DHN ', 'DHANBAD JN ', '1', '03:05', '03:15', '10:00', '259', '2');
Now I want to find to find the trains between two stations on specific date. So I tried with this query
SELECT distinct d1.train_no
FROM train_schedule d1
INNER JOIN train_schedule d2 ON d2.train_no=d1.train_no
WHERE d1.stn_code = 'JU' and d2.stn_code = 'JP'
But it's showing both data from JU to JP and from JP to JU also so it makes the result double.
I want to make this query correct only for one direction on a specific date as days when it runs is also given in database
If i understand your question correctly, to answer the part to return only one direction, please try the query below:
SELECT d1.*
FROM train_schedule d1
INNER JOIN train_schedule d2 ON d2.train_no=d1.train_no
WHERE d1.stn_code = 'JU' and d2.stn_code = 'JP'
AND d1.distance < d2.distance
JU (departure) will always have distance less than JP (arrival).
Instead of sun...sat you should use 1..7 and make the value boolean or tinyint
SELECT distinct d1.train_no
FROM train_schedule d1 WHERE DAYOFWEEK(#yyyy-dd-mm#) = 1
INNER JOIN train_schedule d2 ON d2.train_no=d1.train_no
WHERE d1.stn_code = 'JU' and d2.stn_code = 'JP'
This should work