Laravel query builder - Union All with Skip and Take - php

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)
);

Related

MYSQL - Create single sql query from multiple query

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(...);

compare rows of two query set result in same table

I have table where I store data on basis of date.
Now I need to check that: is there any difference between data of rows with two different dates.
In a simple way you can say that I have two queries which select data from same table now I have to compare each row and column value. for example my two query are -
SELECT * FROM `national` WHERE `upload_date` = '2015-08-04' // return 106 rows
and
SELECT * FROM `national` WHERE `upload_date` = '2015-08-01' // return 106 rows
I have tried to compare this with below query but the result not seem to be correct to me, I am not satisfy with this.
Select U.* from
(SELECT t1.* FROM `national` t1 where upload_date = '2015-08-01'
union all
SELECT t2.* FROM `national` t2 WHERE `upload_date` = '2015-08-04' ) U
GROUP BY emp_id, uqi_id
HAVING COUNT(*) = 1
Can Any one please provide me correct query ?? thank you
Try this
(
SELECT t1.*
FROM
`national` t1, `national` t2
where
t1.upload_date = '2015-08-01' and t2.upload_date='2015-08-04' and
-- put your columns here that you want to compare for same DATA
-- like t1.name=t2.name and etc...
)
You can try with something like that
SELECT * FROM (SELECT * FROM `national` WHERE upload_date = '2015-08-01') a
INNER JOIN (SELECT * FROM `national` WHERE upload_date = '2015-08-04') b
ON a.emp_id = b.emp_id AND a.uqi_id = b.uqi_id
ORDER BY uqi_id

Counting multiple specific value in the same mysql request

Is it possible to do something like this:
SELECT *,
COUNT(* WHERE media_id = 1) as count_1,
COUNT(* WHERE media_id = 2) as count_2
WHERE user_id = 1
or do I have to make separate queries?
Thanks.
You can use conditional sum something as
select
sum(media_id = 1) as count_1,
sum(media_id = 2) as count_2
from table_name
where user_id = 1;
Note that using aggregate function does not make sense with select *
Yup, it's possible.
Try this for example:
select
(select count(*) from TableA where 1=1) as totalA,
(select count(*) from TableB where 1=1) as totalB,
(select count(*) from TableC where 1=1) as totalc

MySQL: Query design issue

My tables are like this:
Table 1 (students)
Table 2 (results)
I want to select all students from Table 1 students who have 4 results in the results table. I tried this query, but with no success:
SELECT *
FROM students
WHERE gender = 'm'
AND (SELECT COUNT( result ) AS count
FROM results
INNER JOIN students ON results.stuID = students.stuID
WHERE result !=0
) =4
ORDER BY rank ASC
You can rewrite your query by using join and HAVING clause to check the count for each student group ,This can be done without using the subquery which sometimes affects on performance
SELECT s.*,COUNT(*) AS count
FROM students s
INNER JOIN results r ON r.stuID = s.stuID
WHERE r.result !=0
GROUP BY s.stuID
HAVING count =4
ORDER BY s.rank ASC
um, that's a little convoluted.
the where clause should come after the subquery, and the subquery still needs to be JOINed back to the main query.
something like
SELECT * FROM students
INNER JOIN (SELECT COUNT(result),results.stuID as count FROM results WHERE result != 0) as result_count
ON result_count.stuID = students.stuID
WHERE result_count.count =4 AND students.gender = 'm'
ORDER BY rank ASC
You have to use alias for table also -
SELECT *
FROM students as a
WHERE gender = 'm'
AND (SELECT COUNT(result) AS count
FROM results as b
WHERE b.stuID = a.stuID AND
(result!=0 OR result IS NOT NULL OR result!='')
) = 4
ORDER BY rank ASC

How to get in MySQL defined names into php?

If I have a mysql query like
(SELECT COUNT(*) FROM data AS amount)
UNION
(SELECT COUNT(*) FROM data WHERE some < 50 AS something)
and then create an array with php like this $row = mysqli_fetch_array($sql, MYSQLI_ASSOC);.
How can I now address each of the AS names. This does not work: echo $row["amount"];. The second question I have is why can't I use AS something when having a WHERE clause?
Try this:
(
SELECT
'amount1' as za_name,
COUNT(*) as za_count
FROM
data
)
UNION
(
SELECT
'amount2' as za_name,
COUNT(*) as za_count
FROM
data
WHERE some < 50
)
Then you can differentiate by $row[za_name] and get the amount $row[za_count]
For the second question : you can use it if you make a temp table :
SELECT
tmp.za_name,
tmp.za_count
FROM (
SELECT
'amount2' as za_name,
COUNT(*) as za_count
FROM
data
WHERE some < 50
) as tmp
In a UNION the row names/aliases for the entire query are whatever they are in the first query.
So if you have
SELECT field1 AS A
UNION
SELECT field2 AS B
Your final result will only have an A field, which will have both field1 and field2.
In your query, you want to alias the COUNT(*), not the table.
(SELECT COUNT(*) AS amount FROM data)
UNION
(SELECT COUNT(*) FROM data WHERE some < 50)
Nor $row['amount'] will be all of the COUNT(*) rows from the entire query.
in your query
(SELECT COUNT(*) FROM data AS amount) UNION (SELECT COUNT(*) FROM data WHERE some < 50 AS something)
You are aliasing the table data to the name amount rather than the sub-query
You have to edit the query this way (aliases on the columns, too)
(SELECT COUNT(*) as amount FROM data)
UNION
(SELECT COUNT(*) as amount FROM data WHERE some < 50 AS something)
This way You are able to address $result['amount'] as a result from the fetch assoc method.

Categories