I am using MYSQL Adminer 4.8.0 4.8.1
I am trying to form a single query from below select queries,
is their any way ir can be done?
There are 4-5 queries so i am afraid
SELECT `value` from `core_config_data`
WHERE `path` = 'catalog/product/base_media_url';
SELECT `label`
FROM `catalog_product_entity_media_gallery_value`
WHERE `value_id` = (SELECT `value_id`
FROM `catalog_product_entity_media_gallery`
WHERE `value` LIKE '%test%')
LIMIT 1;
SELECT `child_id`
FROM `catalog_product_relation`
WHERE `parent_id` = 9622
LIMIT 1;
SELECT `rule_price`
FROM `catalogrule_product_price`
WHERE `product_id` = 9622
LIMIT 1;
SELECT value
FROM `catalog_product_entity_decimal`
WHERE `row_id` = 9622 AND `attribute_id` IN (SELECT `attribute_id` FROM `eav_attribute`
WHERE `attribute_code` IN ('price','special_price'))
SELECT `value`
FROM `core_config_data`
WHERE `scope` = 'websites' AND `path` = 'currency/options/default' AND `scope_id` = 3';
SELECT e.sku,value from catalog_product_entity e
inner join catalog_product_entity_varchar v on e.entity_id = v.row_id and e.entity_id = 9622 and
attribute_id IN (select attribute_id from eav_attribute where attribute_code IN ('name','sku'));
```
It would be very very helpful, if i get single query for this. Not sure if its easy to do it. but i am quite new in mysql so not getting exaclty.
I have tried using UNION but that won't work.
I have tried
SELECT parent.entity_id AS parent_id,
simple.entity_id AS simple_id,
parent.sku AS sku, simple.sku AS simple_sku
ccd.value AS base_media_url
FROM catalog_product_entity AS parent
JOIN catalog_product_super_link AS link ON parent.row_id = link.parent_id
JOIN catalog_product_entity AS simple ON link.product_id = simple.entity_id
LEFT JOIN core_config_data AS ccd ON path = 'catalog/product/base_media_url'
WHERE parent.entity_id IN (9244) LIMIT 1
but it is not working.
Technique 1: Combining scalars:
SELECT a ... LIMIT 1;
SELECT b ... LIMIT 1;
-->
SELECT
( SELECT a ... LIMIT 1) AS a,
( SELECT b ... LIMIT 1) AS b ;
If a is something like COUNT(*), then you know that there will be exactly one result; hence the LIMIT 1 is unnecessary.
If one of those subqueries might not return any rows, then you get NULL.
Technique 2: A select can be used almost anywhere an expression can be used. The above is, technically, an example of such. Also...
SELECT ... WHERE x = ( SELECT ... ) ...
Again, the subquery must return a single row to make that possible.
SELECT ...
WHERE x LIKE CONCAT('%', ( SELECT ... ), '%')
...;
That becomes something like this after the subquery is evaluated:
SELECT
WHERE x LIKE '%foo%'
...;
(It is not efficient, but it works.)
These 3 are similar, but not necessarily efficient as each other:
SELECT ...
WHERE x IN ( SELECT ... )
SELECT ... FROM A
WHERE EXISTS( SELECT ... FROM B
WHERE B.x = A.x )
SELECT ... FROM A JOIN B ON B.x = A.x
This is similar but finds the matching items that are missing from B:
SELECT ... FROM A LEFT JOIN B ON B.x = A.x
WHERE B.id IS NULL
UNION should be used for queries that have similar output:
SELECT x,y FROM A
UNION
SELECT x,y FROM B
That will produce any number of rows from A and any number of rows from B. Duplicates are removed if you use UNION DISTINCT, or kept if you use UNION ALL.
ORDER BY ... LIMIT ... gets tricky. OFFSET gets even trickier.
Performance
Avoid IN ( SELECT ...) it is usually the slower of the three.
Avoid leading wildcards in LIKE; see if FULLTEXT would be a better option.
INDEX(path), INDEX(parent_id, child_id) ("covering"), INDEX(scope, path, scope_id); maybe others, but I need to see SHOW CREATE TABLE.
Avoid EAV schema; it is bad for performance. I added a link; see the many other discussions. Also: http://mysql.rjweb.org/doc.php/eav and http://mysql.rjweb.org/doc.php/index_cookbook_mysql#speeding_up_wp_postmeta
Those items are probably more important (to performance) than combining the statements together. See https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem
Write a Stored Procedure, then CALL it.
Declaration:
DELIMITER //
CREATE PROCEDURE foo(...)
...
BEGIN
statement 1;
statement 2;
statement 3;
statement 4;
statement 5;
END //
DELIMITER ;
Invocation:
CALL foo(...);
Related
Dummy table:
id FileName DateLastSaved
1 Marium.doc 2015-01-01
2 Amna.doc 2016-01-01
3 Marium.doc 2016-01-01
I want the query to return such rows where FileName is unique in the whole table. Rows should be returned for particular date range.
Suppose date ranges are of 2016 only, so third row should not be returned as FileName is not unique.
The query that I have created is:
$presentquery="SELECT * FROM InitialLog i WHERE MDid='$MDid' AND
(DateLastSaved>='$firstdate' AND DateLastSaved<='$presentdate') AND NOT
EXISTS (SELECT id FROM InitialLog i2 WHERE i2.id<i.id AND i.FileName=i2.FileName )";
(Where $firstdate and $presentdate are 2 dates for date ranges)
The query is returning the accurate results but it's taking time to execute. Is there any other way that I can rewrite this query??
(I have table with many rows)
I put this query together and it returns the results very quickly.
Select *
FROM foo
Where (`datelastsaved` > '2015-12-31' && `datelastsaved` < '2017-01-01')
AND `filename` NOT IN (
Select `filename`
FROM foo
GROUP BY `filename`
HAVING COUNT(*) > 1);
The first part is your normal select statement with the where clauses to filter on the dates.
The second part is the NOT IN where the select statement finds all of the ones with duplicate filenames.
Select `filename` FROM foo GROUP BY `filename` HAVING COUNT(*) > 1)
You can get the same logic using a LEFT JOIN and looking for nulls, that is,
$presentquery = "SELECT DISTINCT i.* FROM InitialLog i
LEFT JOIN InitialLog i2 ON i2.id<i.id AND i.FileName=i2.FileName
WHERE i.MDid='$MDid'
AND i.DateLastSaved>='$firstdate'
AND i.DateLastSaved<='$presentdate'
AND i2.id IS NULL";
This way you are doing a single join rather than subquerying against each value in i.
It looks like you are trying to get the data associated with the first occurrence of each file name, this should work:
SELECT *
FROM InitialLog i
WHERE MDid='$MDid'
AND DateLastSaved>='$firstdate'
AND DateLastSaved<='$presentdate'
AND id IN (SELECT MIN(id) FROM InitialLog GROUP BY FileName)
;
Alternatively, you can do a JOIN with the same subquery instead:
SELECT i.*
FROM InitialLog AS i
INNER JOIN (SELECT MIN(id) AS id
FROM InitialLog
GROUP BY FileName
) AS firsts USING (id)
WHERE i.MDid='$MDid'
AND i.DateLastSaved>='$firstdate'
AND i.DateLastSaved<='$presentdate'
;
I would like to know if is possible to return the results from UNION grouped by their alias.
For instance:
(SELECT * FROM table1) AS first
UNION
(SELECT * FROM table2) AS second
so that the result is:
first = contains all table1 rows
second = contains all table2 rows
Practically i want an associative array like this:
[]=>[
'first'=>[table1 results],
'second'=>[table2 results]
]
I tried it but doesn't work. Maybe i'm doing it bad.
Can this be done with a single query or i've to do 2 separated queries.
Thanks.
You cannot do this with union because it removes duplicates. You can with union all.
One way is:
SELECT t.*, 0 as which FROM table1 t
UNION ALL
SELECT t.*, 1 FROM table2 t
ORDER BY which;
If you don't want to see which in the output, use a subquery:
select . . .
from (select t.*, 0 as which from table1 t union all
select t.*, 1 as which from table1 t
) t
order by which;
Here is my query which is taking 17.9397 sec time to get response:
SELECT allbar.iBarID AS iBarID,
allbar.vName AS vName,
allbar.tAddress AS tAddress,
allbar.tDescription AS tDescription,
(SELECT COUNT(*)
FROM tbl_post p
WHERE p.vBarIDs = allbar.iBarID) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance,
(SELECT count(*)
FROM tbl_user
WHERE FIND_IN_SET(allbar.iBarID,vBarIDs)
AND (eType = 'Bartender'
OR eType = 'Bar Manager'
OR eType = 'Bar Owner')) AS countAss,
allbar.eStatus AS eStatus
FROM
(SELECT DISTINCT b.iBarID AS iBarID,
b.vName AS vName,
b.tAddress AS tAddress,
(CASE LENGTH(b.tDescription) WHEN 0 THEN '' WHEN LENGTH(b.tDescription) > 0
AND LENGTH(b.tDescription) < 50 THEN CONCAT(LEFT(b.tDescription, 50),'...') ELSE b.tDescription END) AS tDescription,
b.usbg AS bar_usbg,
b.enhance AS bar_enhance,
b.eStatus AS eStatus
FROM tbl_bar b,
tbl_user u
WHERE b.iBarID <> '-10') AS allbar
I have tried EXPLAIN, here is the result of that:
Can anyone explain me this EXPLAIN result?
You should totaly rewrite that query, it's complete nonsense.
In this part
(SELECT DISTINCT b.<whatever>
FROM tbl_bar b,
tbl_user u
WHERE b.iBarID <> '-10') AS allbar
what you're basically doing is connecting every row from table tbl_bar with every row from tbl_user. Then filter tbl_bar, and when everything is selected (maybe MySQL has to write everything in a temporary table before doing this) return the result set without duplicates. You don't ever want to do that. Especially when you don't even select anything from tbl_user. When there's a connection, specify it. If there's none, don't join those tables or create a connection. I don't know if or how your tables are connected, but it should look something like this:
(SELECT DISTINCT b.<whatever>
FROM tbl_bar b
JOIN tbl_user u ON b.user_id = u.id /*or whatever the connection is*/
WHERE b.iBarID <> '-10') AS allbar
Then you have this ugly subquery.
(SELECT COUNT(*)
FROM tbl_post p
WHERE p.vBarIDs = allbar.iBarID) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance,
which is by the way dependent (see your explain output). Which means, that this subquery is executed for every row of your outer query (yes, the one with the cross join as discussed above). Instead of this subquery, join the table in the outer query and work with GROUP BY.
So far the query should look something like this:
SELECT
b.iBarID AS iBarID,
b.vName AS vName,
b.tAddress AS tAddress,
b.tDescription AS tDescription,
COUNT(*) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance
FROM
tbl_bar b
JOIN tbl_user u ON b.user_id = u.id
JOIN tbl_post p ON p.vBarIDs = b.iBarID
WHERE b.iBarID <> '-10'
GROUP BY b.iBarID
(In fact, this is not really right. Rule is, every column in the SELECT clause should either be in the GROUP BY clause as well or have an aggregate function (like count() or max() applied to it. Otherwise a random row of each group is displayed. But this is just an example. You will have to work out the details.)
Now comes the worst part.
(SELECT count(*)
FROM tbl_user
WHERE FIND_IN_SET(allbar.iBarID,vBarIDs)
AND (eType = 'Bartender'
OR eType = 'Bar Manager'
OR eType = 'Bar Owner')) AS countAss,
allbar.eStatus AS eStatus
The use of FIND_IN_SET() suggests, that you're storing multiple values in one column. Again, you never ever want to do that. Please read this answer to Is storing a delimited list in a database column really that bad? and then redesign your database. I won't help you with this one, as this clearly is stuff for a separate question.
All this didn't really explain the EXPLAIN result. For this question, I would have to write a whole tutorial, which I won't do, since everything is in the manual, as always.
I need to get data from a table using this query:
(select columns
from tableX
join tableZ
on tableX.id1 = tableZ.other_id)
union all
(select columns
from tableX
join tableZ
on tableX.id2 = tableZ.other_id)
LIMIT num1, num2
Without the LIMIT, I get the correct result with my query builder that look like this (let $first be the first query of select and $second is the other select query):
$first->unionAll($second)->get();
When I try to put skip and/or take, the result is not the same as the first query above.
$first->unionAll($second)->take(num1)->skip(num2)->get();
The result query from the above builder (I got it from DB::getQueryLog()) is something like:
(select columns
from tableX
join tableZ
on tableX.id1 = tableZ.other_id LIMIT num1, num2)
union all
(select columns
from tableX
join tableZ
on tableX.id2 = tableZ.other_id)
which ofcourse yield incorrect result. Does anyone knows what is the work around to make the first query? Thanks!
You need to wrap $firstQuery in another query, like this:
$first->unionAll($second);
$rows = DB::table( DB::raw("({$first->toSql()}) as t") )
->mergeBindings($first->getQuery())
->take(num1)->skip(num2)
->get();
This will result in the following query:
select * from (FIRST_QUERY union all SECOND_QUERY) as t limit num1, num2
One Workaround is to use raw query using DB::select and DB::raw in below sample block...
$num1 = 100; // skip first 100 rows
$num2 = 2; // take only 2 rows
$qry_result = DB::select(DB::raw("select * from
(
(select columns from tableX join tableZ on tableX.id1 = tableZ.other_id)
union all
(select columns from tableX join tableZ on tableX.id2 = tableZ.other_id)
) Qry LIMIT :vskip , :vlimit ") , array('vskip'=>$num1,'vlimit'=>$num2)
);
I have a bit of a situation here.
I have a query:
SELECT DISTINCT (testrecurring.id), testrecurring.cxl, testci.cd
FROM testci, testrecurring
WHERE (testci.id = testrecurring.id)
AND testci.x_origin='1'
ORDER BY testrecurring.id DESC;
Now, if a var is not set, I need to do a select on this query, and here is the catch. I need to exclude some id's. Here is how I'm doing it now.
I create a table with that query: create table xxx SELECT * ..... and now the results from my previous query are inside another table called xxx.
Then:
if (!isset($var)) {
$delete = mysql_query("delete from xxx USING xxx, future_recurring where xxx.id = future_recurring.id");
}
and after the records have been deleted I do my final select * from xxx.
This works just fine, the only thing is that I need to redo all this logic by not creating any tables. Maybe doing some joins, I'm not sure how to proceed.
I hope this is not very confusing.
Any ideas?
And now how about this?:
SELECT tr.id, tr.cxl, tci.cd
FROM testci AS tci
INNER JOIN testrecurring AS tr
ON tci.id = tr.id
LEFT OUTER JOIN future_recurring AS fr
ON tr.id = fr.id
WHERE tci.x_origin='1'
AND fr.id IS NULL
GROUP BY tr.id, tr.cxl, tci.cd
ORDER BY tr.id DESC
This only includes results in which the testrecurring.id is NOT FOUND in future_recurring
You just need to add a where condition to exclude the rows you don't want:
SELECT *
FROM testci
JOIN testrecurring on testrecurring.id = testci.id
WHERE testci.x_origin='1'
AND testci.id NOT IN (SELECT id FROM future_recurring)
ORDER BY testrecurring.id DESC;
Or you could try this which might give better performance:
SELECT *
FROM testci
JOIN testrecurring on testrecurring.id = testci.id
LEFT JOIN future_recurring on future_recurring.id = testci.id
WHERE testci.x_origin='1'
AND future_recurring id IS null
ORDER BY testrecurring.id DESC;
Although, if your indexes are good and sane and your data sets aren't enormous then the performance should be close.