Make query inner query result selectable - mysql

I was wondering if it is possible using MySQL to somehow, wrap the select of the inner query so that the outer query can use it in its where clause.
SELECT
`FirstName`,
`Surname`,
`AddressLine1`,
`AddressLine2`,
`AddressLine3`,
`AddressLocale`,
`AddressRegion`,
`AddressZip`,
`AddressCountry`,
`CopyShipAddress`
FROM `Contacts`
WHERE `EEID`
IN
(SELECT CONCAT_WS(',', `Sender`, `Receiver`, `Buyer` ) AS EEID_list
FROM `Transactions`
WHERE `TransactionID` = 3)
Sender, Receiver and Buyer are EEIDs. Perhaps there is a function other than CONCAT_WS I can use that will provide me with this functionality.

dont use concat_ws records on IN query , it may not give correct data
concat_ws may work perfectly for IN query with integers but may not work for strings because they need to be enclosed in quotes '
try below instead
SELECT
`FirstName`,
`Surname`,
`AddressLine1`,
`AddressLine2`,
`AddressLine3`,
`AddressLocale`,
`AddressRegion`,
`AddressZip`,
`AddressCountry`,
`CopyShipAddress`
FROM `Contacts`
WHERE `EEID`
IN
(
select Sender as eeid FROM Transactions WHERE TransactionId=3
UNION ALL
select Receiver as eeid FROM Transactions WHERE TransactionId=3
UNION ALL
select Buyer as eeid FROM Transactions WHERE TransactionId=3
)

Related

MySQL optimized performance for two large tables with same index

I have two tables with huge amount of data in them (~1.8mil in the main one, ~1.2mil in the secondary one), as follows:
subscriber_table (id, name, email, country, account_status, ...)
subscriber_payment_table (id, subscriber_id, payment_type, payment_credential)
My end goal is having a table, containing all the users and their payment tables (null if non existing), up to yesterday, and with account_status = 1 (active)
Mot all subscribers have a corresponding subscriber_payment, so using an INNER JOIN isn't a viable option, and using a LEFT JOIN has me end up with SQL timing out my query after 2 hrs after much processing effort.
SELECT
`subscribers`.`id` AS `id`,
`subscribers`.`email` AS `email`,
`subscribers`.`name` AS `name`,
`subscribers`.`geoloc_country` AS `country`,
`subscribers_payment`.`payment_type` AS `paymentType`,
`subscribers_payment`.`payment_credential` AS `paymentCredential`
`subscribers`.`create_datetime` AS `createdAt`
FROM
`subscribers`
LEFT JOIN
`subscribers_payment` ON (`subscribers_payment`.`subscriberId` = `subscribers`.`id`)
WHERE
`subscribers`.`account_status` = 1
AND DATE_FORMAT(CAST(`subscribers`.`create_datetime` AS DATE), '%Y-%m-%d') < curdate())
As mentioned, this query takes too much time and ends up timing out and not working.
I've also considered having a UNION, between "All the Subscribers" and "Subscribers with Payment".
(
SELECT
`subscribers`.`id` AS `id`,
`subscribers`.`email` AS `email`,
`subscribers`.`name` AS `name`,
`subscribers`.`geoloc_country` AS `country`,
null AS `paymentType`,
null AS `paymentCredential`
`subscribers`.`create_datetime` AS `createdAt`
FROM
`subscribers`
WHERE
`subscribers`.`account_status` = 1
AND DATE_FORMAT(CAST(`subscribers`.`create_datetime` AS DATE), '%Y-%m-%d') < curdate()))
UNION
(
SELECT
`subscribers`.`id` AS `id`,
`subscribers`.`email` AS `email`,
`subscribers`.`name` AS `name`,
`subscribers`.`geoloc_country` AS `country`,
`subscribers_payment`.`payment_type` AS `paymentType`,
`subscribers_payment`.`payment_credential` AS `paymentCredential`
`subscribers`.`create_datetime` AS `createdAt`
FROM
`subscribers`
INNERJOIN
`subscribers_payment` ON (`subscribers_payment`.`subscriberId` = `subscribers`.`id`)
WHERE
`subscribers`.`account_status` = 1
AND DATE_FORMAT(CAST(`subscribers`.`create_datetime` AS DATE), '%Y-%m-%d') < curdate()))
The problem with that current implementation is that I'm getting duplicate queries (I'm using a UNION but it's not grouping my results together and removing non-distinct values, that's because I have a different value in the paymentType and paymentCredential columns)
This query runs in about ~2mins, so this is more feasible for me. I just need to eliminate duplicate records.. unless there's a wiser option here
Disclaimer: we're using MyISAM tables, so having foreign keys to speed up the queries is a no-go.
For this query:
SELECT . . .
FROM subscribers s LEFT JOIN
subscribers_payment sp
ON sp.subscriberId = s.id
WHERE s.account_status = 1 AND
s.create_datetime < curdate();
Then, you want an index on subscribers(account_status, create_datetime, id) and on subscribers_payment(subscriberId).
I am guessing that the index on subscriber_payment is missing, which explains the performance problems.
Notes:
Use table aliases -- they make the query easier to write and read.
There should be no need to convert a datetime to a string for comparison purposes.
There is no need to use backticks for all identifiers. They just make the query harder to write and read.

Returning results from multiple rows into separate variables with one query

Question: What is the quickest way to assign values from multiple rows of a single column into separate variables?
Using MySQL 5.6, I have a table that stores data in regular time intervals. In the insert trigger, I am attempting to detect when, among other similar things, a "peak" in the values has occurred, defined as two consecutive increasing values to a highest value, followed by two consecutive decreasing values.
Because there are several similar calculations to perform on the same data, I am first retrieving the values from a column for the last five rows into variables. I have the ID numbers of the rows.
One way to do this would be individual SELECT queries for each ID. However, I would like to consolidate them into a single query for speed, since the data will be entered into the database in blocks of 40k rows at a time.
Individual Selects:
SET currentValue = (SELECT `value` FROM `table` WHERE `tableid`=currentID);
SET prev1Value = (SELECT `value` FROM `table` WHERE `tableid`=prev1ID);
SET prev2Value = (SELECT `value` FROM `table` WHERE `tableid`=prev2ID);
SET prev3Value = (SELECT `value` FROM `table` WHERE `tableid`=prev3ID);
SET prev4Value = (SELECT `value` FROM `table` WHERE `tableid`=prev4ID);
I know another way to do this would be using joins on the same table. However, this seems like this could be a slow way to go about it. Is there a faster way to do this without using JOINs? Thanks.
Multiple JOINs:
SELECT a0.`value`, a1.`value`, a2.`value`, a3.`value`, a4.`value`
INTO currentValue, prev1Value, prev2Value, prev3Value, prev4Value
FROM (SELECT `value` FROM `table` WHERE `tableid`=currentID) AS a0
INNER JOIN (SELECT `value` FROM `table` WHERE `tableid`=prev1ID) AS a1
INNER JOIN (SELECT `value` FROM `table` WHERE `tableid`=prev2ID) AS a2
INNER JOIN (SELECT `value` FROM `table` WHERE `tableid`=prev3ID) AS a3
INNER JOIN (SELECT `value` FROM `table` WHERE `tableid`=prev4ID) AS a4
Another thing I thought of is something like:
SELECT IF(`tableid`=currentID, `value`, NULL)
, IF(`tableid`=prev1ID, `value`, NULL)
, IF(`tableid`=prev2ID, `value`, NULL)
, IF(`tableid`=prev3ID, `value`, NULL)
, IF(`tableid`=prev4ID, `value`, NULL)
INTO currentValue, prev1Value, prev2Value, prev3Value, prev4Value
FROM `table`
WHERE `tableid` IN (currentID, prev1ID, prev2ID, prev3ID, prev4ID);
But I haven't tested it yet.
For now I am going to go with the JOINs until everything is in place, and I can test the IF statement model. However, if someone knows that isn't going to work or has another method that would work better, I would appreciate it. Also, would putting this into a VIEW help with the speed of the query?
Thanks.

MySQL insert into table with a set of values

I need to insert a table with some values (eg: 'NDA' in this case). This seems to work well if I have just one value to be inserted. I have around a dozen of similar values, is there a was i can tweak this query to insert say { 'NDA', 'SDM', 'APM' } values. Was curious to know if it can be done without a stored procedure or copy pasting the same statements over and changing the values.
INSERT IGNORE INTO customer_feature (customer_id, feature)
SELECT c.id, 'NDA' FROM
customer as c
where c.edition = 'FREE_TRIAL';
Reference: mysql -> insert into tbl (select from another table) and some default values
Is this what you want?
INSERT IGNORE INTO customer_feature(customer_id, feature)
select c.id, f.feature
from customer c cross join
(select 'NDA' as feature union all select 'SDM' union all select 'APM'
) f
where c.edition = 'FREE_TRIAL';

Selecting intersection of two queries with a single map table in MySQL

I'm sure this is easy, but so help me I can't figure out why I can't return the right result.
Pretty standard setup, I have a ref_product table, a ref_tagmap table and a ref_tag table...
CREATE TABLE `ref_product` (
`id` DOUBLE ,
`name` VARCHAR (765),
`familyid` DOUBLE );
INSERT INTO `ref_product` (`id`, `name`, `familyid`) VALUES('264','Old Red Fixture 1','4');
INSERT INTO `ref_product` (`id`, `name`, `familyid`) VALUES('30206','Modern Red Fixture 2','405');
CREATE TABLE `ref_tag` (
`TagID` DOUBLE ,
`TagName` VARCHAR (150));
INSERT INTO `ref_tag` (`TagID`, `TagName`) VALUES('103','Modern Contemporary');
INSERT INTO `ref_tag` (`TagID`, `TagName`) VALUES('131','Red');
CREATE TABLE `ref_tagmap` (
`MapID` DOUBLE ,
`tagid` DOUBLE ,
`containertype` VARCHAR (45),
`containerid` DOUBLE );
INSERT INTO `ref_tagmap` (`MapID`, `tagid`, `containertype`, `containerid`) VALUES('17035','131','PROD','264');
INSERT INTO `ref_tagmap` (`MapID`, `tagid`, `containertype`, `containerid`) VALUES('17747','131','PROD','30206');
INSERT INTO `ref_tagmap` (`MapID`, `tagid`, `containertype`, `containerid`) VALUES('31959','103','PROD','30206');
Querying these tables using:
SELECT DISTINCT ref_product.familyid,ref_tag.tagid
FROM (ref_tag,ref_product)
JOIN ref_tagmap AS mt2 ON mt2.containerid=ref_product.id
AND mt2.containertype='PROD'
AND mt2.tagid=ref_tag.tagid
AND ref_tag.tagname='red'
correctly returns all of the product familyids that have the tag 'red' mapped to them. Similarly:
SELECT DISTINCT ref_product.familyid,ref_tag.tagid
FROM (ref_tag,ref_product)
JOIN ref_tagmap AS mt1 ON mt1.containerid=ref_product.id
AND mt1.containertype='PROD'
AND mt1.tagid=ref_tag.tagid
AND LCASE(ref_tag.tagname)='modern contemporary'
correctly returns the product familyids that have the tag 'modern contemporary' mapped to them. QUESTION IS, HOW DO I RETURN A LIST OF ONLY THE PRODUCT FAMILYIDS THAT HAVE BOTH TAGS MAPPED TO THEM?
I'm trying this, and it returns empty:
SELECT DISTINCT ref_product.familyid,ref_tag.tagid
FROM (ref_tag,ref_product)
JOIN ref_tagmap AS mt2 ON mt2.containerid=ref_product.id
AND mt2.containertype='PROD'
AND mt2.tagid=ref_tag.tagid
AND ref_tag.tagname='red'
JOIN ref_tagmap AS mt1 ON mt1.containerid=ref_product.id
AND mt1.containertype='PROD'
AND mt1.tagid=ref_tag.tagid
AND LCASE(ref_tag.tagname)='modern contemporary'
I have to assume I'm missing something fundamental here...feeling dense. Please help.
Thanks!
The typical way to do this is to ensure that the number of distinct items in the tag table is equal to the number of tags you wish to isolate.
Example:
SELECT p.familyid
FROM ref_product p
JOIN ref_tagmap tm ON tm.containerid=p.id
AND tm.containertype='PROD'
JOIN ref_tag t ON t.tagid = tm.tagid
AND t.tagname IN ('red',
'modern contemporary')
GROUP BY p.familyid
HAVING count(DISTINCT t.tagid) = 2;
In action: http://sqlfiddle.com/#!2/f377e/7

How to multiple select in same table, in single query

I want a single query to multiple select, my purpose is to reduce mysql process, so I use this query, but it does not work:
SELECT *
FROM `my_setting`
WHERE `setting` = `site_url`
AND `setting` = `site_name`
I use select UNION query, but I think it doesn't reduce memory.
I think you want either:
SELECT * FROM my_setting
WHERE `setting` in ('site_url', 'site_name') -- if site_url is a literal string
or
SELECT * FROM my_setting
WHERE `setting` in (site_url, site_name) -- if site_url is a column name
Try this:
SELECT * FORM `my_setting` WHERE (`setting` = `site_url` OR `setting` = `site_name`)
Should get you where you need to go.