How can I move MYSQL query to Laravel Eloquent subquery? - php

My query like this:
SELECT AVG(c.total) FROM (
SELECT COUNT(*) as total, b.`app_id` FROM (
SELECT DISTINCT `app_id`, `insurance_id`
FROM `application`
WHERE `price` IS NOT NULL AND `created_at` >= "2015-11-01 00:00:00"
GROUP BY `app_id`, `insurance_id`
) as b
GROUP BY b.`app_id`
) as c
How can I implement this query to Laravel Eloquent?
My table data is like this;
+------+--------+--------------+---------------+---------------------+
| id | app_id | insurance_id | price | created_at |
+------+--------+--------------+---------------+---------------------+
| 3867 | 33846 | 10 | NULL | 2015-11-20 17:50:26 |
| 3866 | 33846 | 4 | 642,42 | 2015-11-20 17:50:17 |
| 3865 | 33846 | 7 | 687.68 | 2015-11-20 17:50:02 |
| 3864 | 33846 | 8 | NULL | 2015-11-20 17:49:56 |
| 3863 | 33846 | 3 | 540.7500 | 2015-11-20 17:49:55 |
| 3862 | 33846 | 1 | 988.240000000 | 2015-11-20 17:49:54 |
| 3861 | 33846 | 2 | NULL | 2015-11-20 17:49:45 |
| 3860 | 33845 | 10 | 565.80 | 2015-11-20 17:22:25 |
| 3859 | 33845 | 4 | 504,90 | 2015-11-20 17:22:15 |
| 3858 | 33845 | 5 | 459.65 | 2015-11-20 17:22:04 |
| 3856 | 33845 | 7 | 664.58 | 2015-11-20 17:21:56 |
| 3857 | 33845 | 10 | 565.80 | 2015-11-20 17:21:56 |
| 3855 | 33845 | 8 | NULL | 2015-11-20 17:21:54 |
| 3854 | 33845 | 1 | 987.420000000 | 2015-11-20 17:21:48 |
| 3853 | 33845 | 3 | 518.6900 | 2015-11-20 17:21:47 |
| 3852 | 33845 | 2 | NULL | 2015-11-20 17:21:44 |
| 3851 | 33845 | 4 | NULL | 2015-11-20 17:21:31 |
| 3850 | 33845 | 7 | NULL | 2015-11-20 17:21:30 |
| 3849 | 33845 | 5 | 459.65 | 2015-11-20 17:21:26 |
| 3848 | 33845 | 3 | 518.6900 | 2015-11-20 17:21:23 |
+------+--------+--------------+---------------+---------------------+

I think the simplest solution will be to use the DB interface for this.
You could run the query like this:
$averages = \DB::select('SELECT AVG(c.total) FROM (
SELECT COUNT(*) as total, b.`app_id` FROM (
SELECT DISTINCT `app_id`, `insurance_id`
FROM `application`
WHERE `price` IS NOT NULL AND `created_at` >= "2015-11-01 00:00:00"
GROUP BY `app_id`, `insurance_id`
) as b
GROUP BY b.`app_id`
) as c');
You can take a look at what $averages looks like and what it contains and what methods are available for it using php artisan tinker which is a very useful tool for experimenting to find the solutions to problems.
If you want to set the created_at clause dynamically you can do something like this:
$dateString = '2015-10-15 00:00:00';
$averages = \DB::select('SELECT AVG(c.total) FROM (
SELECT COUNT(*) as total, b.`app_id` FROM (
SELECT DISTINCT `app_id`, `insurance_id`
FROM `application`
WHERE `price` IS NOT NULL AND `created_at` >= "?"
GROUP BY `app_id`, `insurance_id`
) as b
GROUP BY b.`app_id`
) as c', [$dateString]);
If you need the results of a query like this turned into a Model, look into Model#hyrdrate, you can even skip using DB and go straight to a model with Model#hydrateRaw, but since this query returns only one column that may not be what you're looking for.

Related

MySql query in same column for different date

Please consider: I am talking about MySQL Database
I have two tables like this:
tbl_stock_info
+-----+--------+
| sId | sName |
+-----+--------+
| 1 | Apple |
| 2 | Google |
| 3 | Yahoo |
+-----+--------+
tbl_stock_data
+------------+----------+-------------+
| date | stock_id | stock_price |
+------------+----------+-------------+
| 2017-01-25 | 1 | 44.7 |
| 2017-01-25 | 3 | 51 |
| 2017-01-25 | 2 | 71.5 |
| 2017-01-24 | 1 | 44.9 |
| 2017-01-24 | 3 | 51.2 |
| 2017-01-24 | 2 | 71.3 |
+------------+----------+-------------+
The Output I am looking for is like this:
+-----+--------+----------+----------+
| sId | sName | Price_25 | Price_24 |
+-----+--------+----------+----------+
| 1 | Apple | 44.7 | 44.9 |
| 2 | Google | 71.5 | 71.3 |
| 3 | Yahoo | 51 | 51.2 |
+-----+--------+----------+----------+
Any assistance would be greatly appreciated.
You can do it with a query like this. it search the newest 2 dates in the table and generate your query, but the column name are fix. if you want also to change them you must use a prepared statement.
SELECT
si.*
,SUM(if(sd1.`date` = ( SELECT DISTINCT `date` FROM tbl_stock_data ORDER BY `date` DESC LIMIT 0,1), sd1.stock_price ,0) ) as lastday
,SUM(if(sd1.`date` = ( SELECT DISTINCT `date` FROM tbl_stock_data ORDER BY `date` DESC LIMIT 1,1), sd1.stock_price ,0) ) as daybefore
FROM tbl_stock_info si
LEFT JOIN tbl_stock_data sd1 ON sd1.stockid = si.sId
GROUP BY si.sId;
sample
MariaDB [l]> SELECT * FROM tbl_stock_info;
+-----+--------+
| sId | sNAme |
+-----+--------+
| 1 | Apple |
| 2 | Google |
| 3 | Yahoo |
+-----+--------+
3 rows in set (0.00 sec)
MariaDB [l]> SELECT * FROM tbl_stock_data;
+----+------------+---------+-------------+
| id | date | stockid | stock_price |
+----+------------+---------+-------------+
| 1 | 2017-01-25 | 1 | 44.70 |
| 2 | 2017-01-25 | 3 | 51.00 |
| 3 | 2017-01-25 | 2 | 71.50 |
| 4 | 2017-01-24 | 1 | 44.90 |
| 5 | 2017-01-24 | 3 | 51.20 |
| 6 | 2017-01-24 | 2 | 71.30 |
| 7 | 2017-01-23 | 1 | 99.00 |
| 8 | 2017-01-22 | 2 | 22.00 |
| 9 | 2017-01-20 | 3 | 33.13 |
+----+------------+---------+-------------+
9 rows in set (0.01 sec)
test
MariaDB [l]> SELECT
-> si.*
-> ,SUM(if(sd1.`date` = ( SELECT DISTINCT `date` FROM tbl_stock_data ORDER BY `date` DESC LIMIT 0,1), sd1.stock_price ,0) ) as lastday
-> ,SUM(if(sd1.`date` = ( SELECT DISTINCT `date` FROM tbl_stock_data ORDER BY `date` DESC LIMIT 1,1), sd1.stock_price ,0) ) as daybefore
-> FROM tbl_stock_info si
-> LEFT JOIN tbl_stock_data sd1 ON sd1.stockid = si.sId
-> GROUP BY si.sId;
+-----+--------+---------+-----------+
| sId | sNAme | lastday | daybefore |
+-----+--------+---------+-----------+
| 1 | Apple | 44.70 | 44.90 |
| 2 | Google | 71.50 | 71.30 |
| 3 | Yahoo | 51.00 | 51.20 |
+-----+--------+---------+-----------+
3 rows in set (0.00 sec)
MariaDB [l]>

force count to be zero in a mysql query

I have two tables t1, t2 and the following query:
SELECT t2.year,
Count(t1.id) AS count
FROM t1,
t2
WHERE t2.t1id = t1.id
AND t2.year IN ( 1995, 1996, 1997, 1998,
1999, 2000 )
GROUP BY t2.year
ORDER BY t1.year
Which results in:
+----------+--------+
| year | count |
+----------+--------+
| 1995 | 1 |
| 1998 | 3 |
| 1999 | 3 |
| 2000 | 28 |
+----------+--------+
And as you can see some years are missing. Is it possible to rewrite this query such that it results in?
+----------+--------+
| year | count |
+----------+--------+
| 1995 | 1 |
| 1996 | 0 |
| 1997 | 0 |
| 1998 | 3 |
| 1999 | 3 |
| 2000 | 28 |
+----------+--------+
I could use php and check which rows are missing to fill in the missing gaps, but that doesn't seem very efficient.. Any ideas?
edit
t1
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(128) | NO | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
t2
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| t1id | int(11) | NO | | NULL | |
| year | int(11) | NO | | NULL | |
+-----------+--------------+------+-----+---------+----------------+
For example:
t1
+----------+---------+
| id | name |
+----------+---------+
| 1 | john |
| 2 | bob |
| .. | .. |
+----------+---------+
t2
+----------+---------+---------+
| id | t1id | year |
+----------+---------+---------+
| 100 | 1 | 1995 |
| 101 | 2 | 1998 |
| 103 | 3 | 1998 |
| .. | .. | .. |
+----------+---------+---------+
Where after the combination I end up with:
+----------+---------+
| id | year |
+----------+---------+
| 100 | 1995 |
| 101 | 1998 |
| 103 | 1998 |
| .. | .. |
+----------+---------+
SELECT t2.year,
IF(Count(t1.id) > 0, Count(t1.id), 0)
FROM t1,
t2
WHERE t2.t1id = t1.id
AND t2.year IN ( 1995, 1996, 1997, 1998,
1999, 2000 )
GROUP BY t2.year
ORDER BY t1.year
Without a source of all possible years that your query could cover you are going to have to use php to do this. One approach would could look something like this.
function getCountsForRange(\PDO $dbConn, $startYear, $endYear){
$ret = array_fill_keys(range($startYear, $endYear), 0);
$stmt = $dbConn->prepare("SELECT t2.year,Count(t1.id) AS count ".
"FROM t1,t2 ".
"WHERE t2.t1id = t1.id AND t2.year between ? and ? ".
"GROUP BY t2.year ORDER BY t1.year");
$stmt->execute([$startYear, $endYear]);
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)){
$ret[$row["year"]] = $row["count"];
}
return $ret;
}
create table yrCheat
( year int not null
);
create table t1
( -- forgive lack of PK
id int not null,
name varchar(128) not null
);
create table t2
( -- forgive lack of PK
id int not null,
t1id int not null,
year int not null
);
insert t1(id,name) values (100,'john'),(101,'bob'),(102,'sally');
insert t2(id,t1id,year) values (100,1,1995),(101,2,1998),(101,3,1998),(101,4,1998);
insert into yrCheat (year) values (1990),(1991),(1992),(1993),(1994),(1995),(1996),(1997),(1998),(1999),(2000);
-- etc
select yc.year,count(t1.id) as count
from yrCheat yc
left join t2
on t2.year=yc.year -- and yc.year in (1995,1996,1997,1998,1999,2000)
left join t1
on t1.id=t2.id
where yc.year in (1995,1996,1997,1998,1999,2000)
group by yc.year
order by yc.year
+------+-------+
| year | count |
+------+-------+
| 1995 | 1 |
| 1996 | 0 |
| 1997 | 0 |
| 1998 | 3 |
| 1999 | 0 |
| 2000 | 0 |
+------+-------+
6 rows in set (0.00 sec)
You will need to handle the empty rows pragmatically or in the query itself depending on the situation.
See:
MySQL GROUP BY and Fill Empty Rows
or
Populating query results with empty rows
For some ideas.

SUM in MySQL with ORDER BY and LIMIT

Suppose I have the following MySQL table:
I would like to know the total volume for Fuji Apples for the 3 days before the latest sale date (like a moving total). So for this example, I am after the total volume for my selection for the 3 days prior to the 04/01/14, which is 9.
I have made a number of attempts without the intended results:
SELECT sum(volume) FROM (SELECT `volume` FROM `fruit_sale_db` WHERE `fruit` = 'apple' AND `type` = 'fuji') AS subquery ORDER BY `date` DESC LIMIT 1,3
I thought ORDER BY date DESC LIMIT 1,3 would work by restricting dates to 3 starting from the second last entry but it doesn't work.
SELECT sum(volume) FROM `fruit_sale_db` where 'date' >= (latest_sale_date - 3) and 'date' <= latest_sale_date and `fruit` = 'apple' AND `type` = 'fuji'
and latest_sale_date would be something like
SELECT `date` FROM `fruit_sale_db` WHERE `fruit` = 'apple' AND `type` = 'fuji' ORDER BY `date` DESC LIMIT 1
Consider the following...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,Fruit VARCHAR(12) NOT NULL
,Type VARCHAR(12) NOT NULL
,Date DATE NOT NULL
,Volume INT NOT NULL
);
INSERT INTO my_table (fruit,type,date,volume) VALUES
('Apple','Fuji' ,'2014-01-01',1),
('Apple','Other','2014-01-01',6),
('Apple','Fuji' ,'2014-01-01',2),
('Apple','Other','2014-01-02',1),
('Apple','Other','2014-01-02',4),
('Apple','Fuji' ,'2014-01-03',4),
('Apple','Other','2014-01-03',2),
('Apple','Fuji' ,'2014-01-04',8),
('Apple','Fuji' ,'2014-01-05',16),
('Pear' ,'Other','2014-01-06',1),
('Apple','Other','2014-01-06',4),
('Apple','Fuji' ,'2014-01-07',32),
('Apple','Other','2014-01-07',2),
('Apple','Fuji' ,'2014-01-08',64);
SELECT * FROM my_table;
+----+-------+-------+------------+--------+
| id | Fruit | Type | Date | Volume |
+----+-------+-------+------------+--------+
| 1 | Apple | Fuji | 2014-01-01 | 1 |
| 2 | Apple | Other | 2014-01-01 | 6 |
| 3 | Apple | Fuji | 2014-01-01 | 2 |
| 4 | Apple | Other | 2014-01-02 | 1 |
| 5 | Apple | Other | 2014-01-02 | 4 |
| 6 | Apple | Fuji | 2014-01-03 | 4 |
| 7 | Apple | Other | 2014-01-03 | 2 |
| 8 | Apple | Fuji | 2014-01-04 | 8 |
| 9 | Apple | Fuji | 2014-01-05 | 16 |
| 10 | Pear | Other | 2014-01-06 | 1 |
| 11 | Apple | Other | 2014-01-06 | 4 |
| 12 | Apple | Fuji | 2014-01-07 | 32 |
| 13 | Apple | Other | 2014-01-07 | 2 |
| 14 | Apple | Fuji | 2014-01-08 | 64 |
+----+-------+-------+------------+--------+
SELECT a.*
, SUM(b.volume) rolling
, GROUP_CONCAT(b.volume ORDER BY b.id DESC SEPARATOR '+' ) math
FROM my_table a
LEFT
JOIN my_table b
ON b.fruit = a.fruit
AND b.type = a.type
AND b.date BETWEEN a.date - INTERVAL 3 DAY AND a.date - INTERVAL 1 DAY
GROUP
BY a.id
ORDER
BY fruit, type, id DESC;
+----+-------+-------+------------+--------+---------+-------+
| id | Fruit | Type | Date | Volume | rolling | math |
+----+-------+-------+------------+--------+---------+-------+
| 14 | Apple | Fuji | 2014-01-08 | 64 | 48 | 32+16 |
| 12 | Apple | Fuji | 2014-01-07 | 32 | 24 | 16+8 |
| 9 | Apple | Fuji | 2014-01-05 | 16 | 12 | 8+4 |
| 8 | Apple | Fuji | 2014-01-04 | 8 | 7 | 4+2+1 |
| 6 | Apple | Fuji | 2014-01-03 | 4 | 3 | 2+1 |
| 3 | Apple | Fuji | 2014-01-01 | 2 | NULL | NULL |
| 1 | Apple | Fuji | 2014-01-01 | 1 | NULL | NULL |
| 13 | Apple | Other | 2014-01-07 | 2 | 4 | 4 |
| 11 | Apple | Other | 2014-01-06 | 4 | 2 | 2 |
| 7 | Apple | Other | 2014-01-03 | 2 | 11 | 4+1+6 |
| 5 | Apple | Other | 2014-01-02 | 4 | 6 | 6 |
| 4 | Apple | Other | 2014-01-02 | 1 | 6 | 6 |
| 2 | Apple | Other | 2014-01-01 | 6 | NULL | NULL |
| 10 | Pear | Other | 2014-01-06 | 1 | NULL | NULL |
+----+-------+-------+------------+--------+---------+-------+
http://sqlfiddle.com/#!2/5243e/1

GET AND JOIN two data from two different table for If conditions PHP

I have two tables
table A (result a view from some table) :
Query from table A :
SELECT view_person_schedule., view_history_absen., TIMEDIFF(time_in,schedule_time_in) AS late, TIMEDIFF(schedule_time_out,time_out) as overtime FROM person JOIN view_history_absen ON view_history_absen.ID = view_person_schedule.ID
table A :
ID | dates | time_in | time_out |
1 | 2014-06-01 | 07:00 | ........ |
1 | 2014-06-02 | 08:00 | ........ |
1 | 2014-06-03 | 08:10 | ........ |
1 | 2014-06-04 | ..... | ........ |
1 | 2014-06-05 | ..... | ........ |
1 | 2014-06-10 | ..... | ........ |
1 | 2014-06-14 | ..... | ........ |
table B :
ID | dates_exc | information |
1 | 2014-06-06 | NULL |
1 | 2014-06-07 | NULL |
1 | 2014-06-08 | NULL |
1 | 2014-06-09 | NULL |
1 | 2014-06-11 | SICK |
1 | 2014-06-12 | SICK |
1 | 2014-06-13 | SICK |
My question is : how to make result data to be like this below :
ID | dates | time_in | time_out |
1 | 2014-06-01 | 07:00 | ........ |
1 | 2014-06-02 | 08:00 | ........ |
1 | 2014-06-03 | 08:10 | ........ |
1 | 2014-06-04 | ..... | ........ |
1 | 2014-06-05 | ..... | ........ |
1 | 2014-06-06 | NULL | ........ |
1 | 2014-06-07 | NULL | ........ |
1 | 2014-06-08 | NULL | ........ |
1 | 2014-06-09 | NULL | ........ |
1 | 2014-06-10 | ..... | ........ |
1 | 2014-06-11 | SICK | ........ |
1 | 2014-06-12 | SICK | ........ |
1 | 2014-06-13 | SICK | ........ |
1 | 2014-06-14 | ..... | ........ |
Please anyone help me.
Assuming there are no overlapping dates in the two tables, or you want both rows in the result when there are, use a UNION:
SELECT id, dates, time_in, time_out
FROM TableA
UNION
SELECT id, dates_exc, information, NULL
FROM TableB
If Table B should take precedence when there's overlap:
SELECT dates.id, dates.dates, IFNULL(b.information, a.time_in) AS time_in, a.time_out
FROM (SELECT id, dates
FROM tableA
UNION DISTINCT
SELECT id, dates
FROM tableB) AS dates
LEFT JOIN tableB AS b ON dates.id = b.id AND dates.dates = b.dates
LEFT JOIN tableA AS a ON dates.id = a.id AND dates.dates = a.dates

How to combine this two MySQL query using Group By

I have this table that i use to query by grouping via station_id.
+------------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------+---------------+------+-----+---------+-------+
| id | varchar(50) | NO | PRI | NULL | |
| station_id | tinyint(3) | NO | | NULL | |
| game_type_id | smallint(1) | NO | MUL | NULL | |
| price | decimal(10,2) | YES | | 0.00 | |
| date_created | datetime | YES | MUL | NULL | |
| bet_no1 | tinyint(2) | YES | | 0 | |
| bet_no2 | tinyint(2) | YES | | 0 | |
+------------------+---------------+------+-----+---------+-------+
Here is the query i use to display it on a table using GROUP BY station_id
SELECT station_id,
COUNT(*) as bet_counts,
FORMAT(SUM(price),2) as gross
FROM bets
WHERE bet_void=0
AND date_created >= '2013-02-12 00:00:00'
AND date_created < '2013-02-23 00:00:00'
GROUP BY station_id
The query will give me.
+------------+------------+-------+
| station_id | bet_counts | gross |
+------------+------------+-------+
| 1 | 14 | 16.00 |
| 2 | 5 | 5.00 |
| 7 | 11 | 11.00 |
+------------+------------+-------+
But i also have another query that counts each specific bets( game_type_id ) from each station_id. I usually query this inside the a looping statement.
SELECT COUNT(*) as count
FROM bets
WHERE game_type_id = 1
AND station_id = {station_id from first query}
AND date_created >= '2013-02-12 00:00:00'
AND date_created < '2013-02-23 00:00:00'
My question is, how can i make this in one query and still use the GROUP BY station_id and also get the count of bets on each game_type_id? Something like this result.
+------------+------------+-------+-------------------------+-------------------------+
| station_id | bet_counts | gross | count_of_game_type_id_1 | count_of_game_type_id_2 |
+------------+------------+-------+-------------------------+-------------------------+
| 1 | 14 | 16.00 | 10 | 4 |
| 2 | 5 | 5.00 | 3 | 2 |
| 7 | 11 | 11.00 | 11 | 0 |
+------------+------------+-------+-------------------------+-------------------------+
You can do this by joining the results together. However, the logic in the two queries is very similar, so you can combine them into a single aggregation query:
SELECT station_id,sum(case when bet_void = 0 then 1 else 0 end) as bet_counts,
FORMAT(SUM(case when bet_void = 0 then price else 0 end),2) as gross,
sum(case when game_type_id = 1 then 1 else 0 end) as count
FROM bets b
where date_created >= '2013-02-12 00:00:00' AND date_created < '2013-02-23 00:00:00'
GROUP BY station_id

Categories