Hello I have this table
TABLE `record` (
`zpravaID` int(11) NOT NULL AUTO_INCREMENT,
`sensorID` int(11) NOT NULL,
`date_time` datetime NOT NULL,
`cena` double DEFAULT NULL,
`is_cheap` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`zpravaID`), ...)
I would like to do an update which will for example take all records from 2016 and every record, which will be on Monday from 14:00:00 to 14:45:00 will have set the is_cheap to true (1).
I really have no idea, how to do that. Any ideas?
To extract the day of the week you can either use WEEKDAY() or DAYOFWEEK() , the difference between them is that in DAYOFWEEK() sunday is 1 . You can use DATE_FORMAT to extract only the time value, and YEAR() to extract the year of the date :
UPDATE `record` t
SET t.is_cheap = 1
WHERE DAYOFWEEK(t.date_time) = 2
AND DATE_FORMAT(t.date_time,'%H:%i:%s') BETWEEN '14:00:00' AND '14:45:00'
AND YEAR(t.date_time) = 2016
Try this:
update record
set is_cheap = 1
where YEAR(date_time) = 2016
and DAYNAME(date_time) = 'Monday'
and DATE_FORMAT(date_time,'%H:%i:%s') BETWEEN '14:00:00' AND '14:45:00'
Related
I am working with agricultural product management system. I have a question regarding a MySQL query. I would like to know how to create the same query using Laravel query builder:
SELECT
vegitables.name, vegitables.image, vegitables.catagory,
AVG(price_wholesale),
SUM(CASE WHEN rank = 1 THEN price_wholesale ELSE 0 END) today,
SUM(CASE WHEN rank = 2 THEN price_wholesale ELSE 0 END) yesterday
FROM (
SELECT
veg_id, price_wholesale, price_date,
RANK() OVER (PARTITION BY veg_id ORDER BY price_date DESC) as rank
FROM old_veg_prices
) p
INNER JOIN vegitables ON p.veg_id = vegitables.id
WHERE rank in (1,2)
GROUP BY veg_id
This Output result get when run query in database:
Following two table are used to get today price yesterday price and price average get from each product.
CREATE TABLE `vegitables` (
`id` bigint(20) UNSIGNED NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`image` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`catagory` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`total_area` int(11) NOT NULL COMMENT 'Total area of culativate in Sri Lanka (Ha)',
`total_producation` int(11) NOT NULL COMMENT 'Total production particular product(mt)',
`annual_crop_count` int(11) NOT NULL COMMENT 'how many time can crop pre year',
`short_dis` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `vegitables`
ADD PRIMARY KEY (`id`);
ALTER TABLE `vegitables`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
COMMIT;
CREATE TABLE `old_veg_prices` (
`id` bigint(20) UNSIGNED NOT NULL,
`veg_id` int(11) NOT NULL,
`price_wholesale` double(8,2) NOT NULL,
`price_retial` double(8,2) NOT NULL,
`price_location` int(11) NOT NULL,
`price_date` date NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `old_veg_prices`
ADD PRIMARY KEY (`id`);
ALTER TABLE `old_veg_prices`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;
COMMIT;
I try this site to convert to MySQL query to query builder code. But it show some error's could find it out. Any Way i want to run this code in Laravel with any method??
Your query will not return the data for yesterday and today; it will return the data for two most recent dates (e.g. if today is 2021-11-01 and most recent two dates for for carrots are 2021-10-25 and 2021-10-20 it will use those two dates). Using RANK() ... IN (1, 2) is also incorrect because it can return ranks such as 1 followed by 3 instead of 2.
To get today and yesterday prices you don't need window functions. Just use appropriate where clause and conditional aggregation:
SELECT vegitables.name
, vegitables.image
, vegitables.catagory
, AVG(old_veg_prices.price_wholesale) AS avgwholesale
, SUM(CASE WHEN old_veg_prices.price_date = CURRENT_DATE - INTERVAL 1 DAY THEN old_veg_prices.price_wholesale END) AS yesterday
, SUM(CASE WHEN old_veg_prices.price_date = CURRENT_DATE THEN old_veg_prices.price_wholesale END) AS today
FROM vegitables
INNER JOIN old_veg_prices ON vegitables.id = old_veg_prices.veg_id
WHERE old_veg_prices.price_date IN (CURRENT_DATE - INTERVAL 1 DAY, CURRENT_DATE)
GROUP BY vegitables.id -- other columns from vegitables table are functionally dependent on primary key
The Laravel equivalent would be:
DB::table('vegitables')
->Join('old_veg_prices', 'old_veg_prices.veg_id', '=', 'vegitables.id')
->whereRaw('old_veg_prices.price_date IN (CURRENT_DATE - INTERVAL 1 DAY, CURRENT_DATE)')
->select(
'vegitables.name',
'vegitables.image',
'vegitables.catagory',
DB::raw('AVG(old_veg_prices.price_wholesale) AS avgwholesale'),
DB::raw('SUM(CASE WHEN old_veg_prices.price_date = CURRENT_DATE - INTERVAL 1 DAY THEN old_veg_prices.price_wholesale END) AS yesterday'),
DB::raw('SUM(CASE WHEN old_veg_prices.price_date = CURRENT_DATE THEN old_veg_prices.price_wholesale END) AS today')
)
->groupBy(
'vegitables.id',
'vegitables.name',
'vegitables.image',
'vegitables.catagory'
)
->get();
"Query builder" features of abstraction products often leave out some possible SQL constructs. I recommend you abandon the goal of reverse engineering SQL back to Laravel and simply perform the "raw" query.
Also...
rank() OVER (PARTITION BY veg_id ORDER BY price_date DESC) as rank
requires MySQL 8.0 (MariaDB 10.2).
And suggest you avoid the alias "rank" since that is identical to the name of a function.
i need to get the next day time and date for library based on the following table in MySQL without using procedures
CREATE TABLE `library_timing` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`store_id` int(10),
`day` varchar(10) DEFAULT NULL,
`start_time` time DEFAULT NULL,
`end_time` time DEFAULT NULL,
PRIMARY KEY (`id`)
);
Insert into library_timing values(1,1,"Monday",'09:00:00','18:00:00');
Insert into library_timing values(2,1,"Tuesday",'09:00:00','18:00:00');
Insert into library_timing values(3,1,"Thrusday",'09:00:00','18:00:00');
Insert into library_timing values(4,1,"Friday",'09:00:00','18:00:00');
As u can see I have timings day for library
so the output expected is as follows
(Consider Today is MONDAY 25 June 2020)
ID. Next opening Time
1 26-06-2020 09:00:00
And (Consider if Today is TUESDAY 26 JUNE 2020 and WEDNESDAY IS HOLIDAY so DATE TIME FOR THURSDAY)
ID. Next opening Time
1 28-06-2020 09:00:00
i have tried this case so far which gives me next days time but not sure how to do if there is gap of more than 1 day
case
when
(curtime() < start_time or curtime() > end_time) or
(start_time = "00:00:00" and end_time = "00:00:00") or
(start_time is null and end_time is null)
then
UNIX_TIMESTAMP((
select TIMESTAMP(curdate()+1,(
select start_time
from library_timing a
where a.store_id = s.id and day = DAYNAME(CURDATE() + 1)
))
))
else 0 end nextopeningtime
I can advice next solution:
add column day_of_week for number day of week for simplify calcualtion:
CREATE TABLE `library_timing` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`store_id` INT(10),
`day_of_week` INT(10),
`day` VARCHAR(10) DEFAULT NULL,
`start_time` TIME DEFAULT NULL,
`end_time` TIME DEFAULT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO library_timing VALUES(1,1,2,"Monday",'09:00:00','18:00:00');
INSERT INTO library_timing VALUES(2,1,3,"Tuesday",'09:00:00','18:00:00');
INSERT INTO library_timing VALUES(3,1,4,"Thrusday",'09:00:00','18:00:00');
INSERT INTO library_timing VALUES(4,1,5,"Friday",'09:00:00','18:00:00');
next we can use next approach:
SELECT * FROM (
-- check currently open
SELECT 'Open' , `lt`.*
FROM `library_timing` `lt`
WHERE
`lt`.`day_of_week` = DAYOFWEEK('2020-06-29') AND -- '2020-06-29' will be changed to CURRDATE()
CURTIME() BETWEEN `start_time` AND `end_time`
UNION
-- check open next day
SELECT * FROM (
SELECT 'Next' , `lt`.*
FROM `library_timing` `lt`
WHERE `lt`.`day_of_week` > DAYOFWEEK('2020-06-29') -- '2020-06-29' will be changed to CURRDATE()
LIMIT 1
) nextday
UNION
-- check first working day
SELECT 'Next' , `lt`.*
FROM `library_timing` `lt`
WHERE `lt`.`day_of_week` = 2
) opentime LIMIT 1;
This approach not ideal and can be improved
I'm trying to find a row immediately before and after a given row, with a order by clause. The use case is "previous entry" and "next entry" links in a vaguely blog-like system. The engine is MySQL 5.6. The table schema is
CREATE TABLE `weekly_notes` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`year` int(4) DEFAULT NULL,
`week_number` int(11) DEFAULT NULL,
`header_text` text NOT NULL,
`image_filename` varchar(128) DEFAULT NULL,
`boundry_image_filename` varchar(128) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
and the query is ordered by year desc, week_number desc. The criteria for selecting a row is year and week_number.
Sample Data
insert into weekly_notes values
(101,2018,53,'Week 53 from year 2018',NULL,NULL),
(102,2019,50,'Week 50 from year 2019', NULL, NULL),
(103,2019,51,'Week 51 from year 2019', NULL, NULL),
(104,2019,52,'Week 52 from year 2019', NULL, NULL),
(105,2020,1,'Week 1 from year 2020', NULL, NULL),
(106,2019,53,'Week 53 from year 2019', NULL, NULL),
(107,2020,2,'Week 2 from year 2020', NULL, NULL),
(108,2020,3,'Week 3 from year 2020', NULL, NULL),
(109,2020,4,'Week 4 from year 2020', NULL, NULL);
The select criteria are year an week, so I would like to be able to select the week "before" 2020-01 and get the row for 2019-53 or the week before 2020-03 and get the row for 2020-02
You can use lag() -- on however you are defining the ordering. For instance, if you wanted the row after a certain header_text:
select wn.*
from (select wn.*,
lag(header_text) over (order by year, week) as prev_header_text
from weekly_notes wn
) wn
where prev_header_text = <what you are looking for>
This assumes that "after" is chronological. I suppose "after" with a descending sort could actually mean the row before, in which case you would use lead() instead of lag().
For before a particular week/year combo, you can use:
select wn.*
from (select wn.*,
lag(week) over (order by year, week) as prev_week,
lag(year) over (order by year, week) as prev_year
from weekly_notes wn
) wn
where prev_week = #week and prev_year = #year
I have the following table below and would like to take the monthly evolution (%) of total transactions per month. I researched the LAG function but could not understand very well.
I need the return of this query to be like this (Desired Output):
MONTH | TOTAL TRANSACTIONS | % EVOLUTION
----------------------------------------
09 | 45.561 | 0%
10 | 48.598 | 6.66%
UPDATE
% EVOLUTION = ((current value - previous value) / previous value) *
100
It is the formula I use to calculate the evolution of the number of transactions from one month to the previous month.
That is, a column with the previous month's total is required.
DDL
CREATE TABLE IF NOT EXISTS `campanha` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ano_mes` date DEFAULT NULL,
`nome` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
`cpf` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`conta` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`valor` float(10,2) UNSIGNED ZEROFILL NOT NULL,
`transacoes` int(255) DEFAULT NULL,
PRIMARY KEY (`id`)
)
can anybody help me?
MySQL Version: 5.7.23 - MySQL Community Server
http://sqlfiddle.com/#!9/73f38f/2
MySQL 5.x does not support window functions such as LAG. You'll have to do it the old way. Based on table structure the following query should get you started:
SELECT yyyy
, mm
, txn_this_month
, (txn_this_month - (
SELECT SUM(transacoes)
FROM campanha
WHERE ano_mes >= STR_TO_DATE(CONCAT_WS('-', yyyy, mm, 1),'%Y-%c-%e') - INTERVAL 1 MONTH
AND ano_mes < STR_TO_DATE(CONCAT_WS('-', yyyy, mm, 1),'%Y-%c-%e')
)) / txn_this_month * 100 AS perc_change
FROM (
SELECT EXTRACT(YEAR FROM ano_mes) AS yyyy
, EXTRACT(MONTH FROM ano_mes) AS mm
, SUM(transacoes) AS txn_this_month
FROM campanha
GROUP BY EXTRACT(YEAR FROM ano_mes), EXTRACT(MONTH FROM ano_mes)
) AS x
ORDER BY yyyy, mm
The subquery inside select calculates sum for previous month for each row.
I have a table with orders data:
CREATE TABLE `orders` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL DEFAULT '0',
`price` decimal(6,3) unsigned NOT NULL,
`created` datetime DEFAULT NULL,
`paid` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
I need to get price sum of orders in 12 weeks periods, where first period starts on a week when the very first order was paid, and each next period starts one week later than previous (this to have possibility to compare data in 12 weeks periods). So eventually there must be periods like 1-12, 2-13, etc.
Important detail: I can't use variables in query, because this query will be used in BI Tool, where queries with variables behave unexpected.
Did it next way:
SELECT `periods`.*, SUM(`orders`.`price`) AS `revenue`
FROM (SELECT DISTINCT FROM_DAYS(TO_DAYS(`paid`) - MOD(TO_DAYS(`paid`) -1, 7)) AS `period_start`,
(FROM_DAYS(TO_DAYS(`paid`) - MOD(TO_DAYS(`paid`) -1, 7)) + INTERVAL 12 WEEK) AS `period_end`
FROM `orders`
) AS `periods`
LEFT JOIN `orders` ON DATE(`orders`.`paid`) BETWEEN `periods`.`period_start` AND `periods`.`period_end`
GROUP BY `periods`.`period_end`
Small explanation: at first I define 12 week periods in subquery, using same table data. By doing this
DISTINCT FROM_DAYS(TO_DAYS(`paid`) - MOD(TO_DAYS(`paid`) -1, 7))
I shift paid value to the beginning of week.
With this
(FROM_DAYS(TO_DAYS(`paid`) - MOD(TO_DAYS(`paid`) -1, 7)) + INTERVAL 12 WEEK)
I add 12 weeks to the date means beginning of week.
Eventually I get result like this:
| period_start | period_end |
| 2015-07-19 | 2015-10-11 |
| 2015-07-26 | 2015-10-18 |
Then I simply join orders table and group data by end of period.
Looks like it does what I need.