Basically, I have these 2 queries:
SELECT * FROM table
WHERE langue = 'fr' AND hDate IS NULL
LIMIT 12;
UPDATE table
SET hDate = CURDATE() + INTERVAL 1 DAY
WHERE hDate IS NULL
LIMIT 12;
These works good for first 12 NULL records. If I need to update next 12 NULL records I have to manually change the UPDATE query to INTERVAL 2 DAY
Problem is that I have 4000 records.
I have tried
UPDATE table t1 JOIN
(
SELECT id, #n := #n + 1 rnum
FROM table CROSS JOIN (SELECT #n := 0) i
WHERE langue = 'fr'
ORDER BY id
) t2 ON t1.id = t2.id CROSS JOIN
(
SELECT MAX(hDate) sdate FROM table
) q
SET t1.hDate = q.sdate + INTERVAL t2.rnum DAY
from this answer: MySQL query to update records with incremented date
but this increments each record with 1 day. I have to increment 12 records with same date, next 12 records with date + 1, next 12 records with date + 2 etc.
Table definition
CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`texte` mediumtext,
`langue` varchar(9) DEFAULT NULL,
`hDate` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6726 DEFAULT CHARSET=utf8;
Thanks for your help.
http://sqlfiddle.com/#!9/7a554/1
SET #i:=0;
SET #j:=0;
UPDATE t1
RIGHT JOIN (
SELECT
id
,IF(#j = 0 ,#j:=1, #j:=#j+1)
,IF((#j-1) % 12 = 0, #i:= #i+1, #i) as i
FROM t1
WHERE hDate IS NULL
) idx
on idx.id = t1.id
SET t1.hDate = CURDATE() + INTERVAL (idx.i) DAY
Related
I have a source data like:
CREATE TABLE `test` (
`startdate` varchar(100) DEFAULT NULL,
`stopdate` varchar(100) DEFAULT NULL,
`code` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO test (startdate,stopdate,code) VALUES
('20200630','20200731','a01')
,('20200701','2020731','a02')
,('20200702','20200801','a03')
,('20200901','20201001','a04')
,('20200629','20200701','a05')
,('20200621','20200628','a06')
;
I need to get data for every day between 20200701 and 20200703:
select '0701' as a,count(*) as b from test where startdate <= 20200701 and stopdate >= 20200701
union
select '0702' as a,count(*) as b from test where startdate <= 20200702 and stopdate >= 20200702
union
select '0703' as a,count(*) as b from test where startdate <= 20200703 and stopdate >= 20200703
But the problem is I actually have lots of data, I can not use this union one by one.
How to optimize this statement?
Join with a synthesized table that lists all the dates you want to compare with.
SELECT RIGHT(x.date,4) AS a, COUNT(*) AS b
FROM test
JOIN (
SELECT '20200701' AS date
UNION
SELECT '20200702' AS date
UNION
SELECT '20200703' AS date
) AS x ON x.date BETWEEN test.startdate AND test.stopdate
GROUP BY x.date
A bit clumsy because working with varchars that contain a data, but:
with recursive sel as (
select CONVERT('20200701',CHAR(20)) as d
union all
select date_format(adddate(d,interval 1 day),'%Y%m%d')
from sel
where d< '20200703')
select d, count(*)
from sel
left join test on startdate <= d and stopdate >=d
group by d;
Lets suppose MySQL table is structured as follows:
(Timestamp | Value)
(4h00 | 3) ;
(5h00 | 5) ;
(6h00 | 0) ;
(7h00 | 0) ;
(8h00 | 2) ;
(9h00 |10) ;
(10h00 | 0) ;
(11h00 | 3) ;
I want to get the line where (Value != 0) before any appearance of (Value = 0) AND the first line where (Value != 0) after the appearance of (Value = 0).
Means in this case, the query should return
(5h00 | 5) ;
(8h00 | 2) ;
(9h00 | 10) ;
(11h00 | 3) ;
this is the structure of my table :
CREATE TABLE IF NOT EXISTS `latency` (
`key` int(100) NOT NULL AUTO_INCREMENT,
`idReport` varchar(45) NOT NULL,
`timestamp` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`value` float NOT NULL,
`loss` float NOT NULL,
PRIMARY KEY (`idReport`,`timestamp`),
UNIQUE KEY `key` (`key`)
)
SELECT * FROM `table` AS tcurrent
INNER JOIN `table` AS tnext
ON tnext.timestamp = tcurrent.timestamp+60*60
WHERE tnext.value = 0
AND tcurrent.value != 0
You may adapt the +60*60: a DATE_ADD for 1 HOUR is easier to read. Also, do not forget to index columns properly (a UNIQUE INDEX (timestamp, value) sounds good).
You could use row numbering logic , something like this
SELECT T.*
FROM
(
SELECT DISTINCT RN FROM
(
SELECT L.*,
IF (L.VALUE <> #P , #RN:=#RN+1,#RN) RN ,
#P:=L.VALUE P
FROM (SELECT #RN:=0, #P:=0 ) RN,`LATENCY` L
#ORDER BY
) S
WHERE S.VALUE = 0
) S1
JOIN
(SELECT L.*,
IF (L.VALUE <> #P1 , #RN1:=#RN1+1,#RN1) RN ,
#P1:=L.VALUE P
FROM (SELECT #RN1:=0, #P1:=0 ) RN,`LATENCY` L
#ORDER BY
) T
ON S1.RN = T.RN-1 OR S1.RN = T.RN+1
The sub queries s and s1 identify blocks with value = 0 and the join simply picks adjacent row numbers.
I have the following query, and I would like to get 100 items from the database, but host_id is in the urls table many times, and I would like to get a maximum of 10 unique rows from that table per host_id.
select *
from urls
join hosts using(host_id)
where
(
last_run_date is null
or last_run_date <= date_sub(curdate(), interval 30 day)
)
and ignore_url != 1
limit 100
So, I would like:
Maximum Results = 100
Max Rows Per Host = 10
I am not sure what I would need to do to accomplish this task. Is there a way to do this without a subquery?
Hosts Table
CREATE TABLE `hosts` (
`host_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`host` VARCHAR(50) NOT NULL,
`last_fetched` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`ignore_host` TINYINT(1) UNSIGNED NOT NULL,
PRIMARY KEY (`host_id`),
UNIQUE INDEX `host` (`host`)
)
Urls Table
CREATE TABLE `urls` (
`url_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`parent_url_id` INT(10) UNSIGNED NOT NULL,
`scheme` VARCHAR(5) NOT NULL,
`host_id` INT(10) UNSIGNED NOT NULL,
`path` VARCHAR(500) NOT NULL,
`query` VARCHAR(500) NOT NULL,
`date_found` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_run_date` DATETIME NULL DEFAULT NULL,
`ignore_url` TINYINT(1) UNSIGNED NOT NULL,
PRIMARY KEY (`url_id`),
UNIQUE INDEX `host_path_query` (`host_id`, `path`, `query`)
)
Thats it (I hope)
I cant test i real. i have no data. pls test it and give me a little ping.
SELECT *
FROM (
SELECT
#nr:=IF(#lasthost = host_id, #nr+1, 1) AS nr,
u.*,
#lasthost:=IF(#lasthost = host_id, #lasthost, host_id) AS lasthost
FROM
urls u,
( SELECT #nr:=4, #lasthost:=-1 ) AS tmp
WHERE (
last_run_date IS NULL
OR last_run_date <= date_sub(curdate(), INTERVAL 30 DAY)
)
AND ignore_url != 1
ORDER BY host_id, last_run_date
) AS t
LEFT JOIN HOSTS USING(host_id)
WHERE t.nr < 11
LIMIT 100;
ok,
first:
I only select the rows with your query, and order it
by the host_id and time
SELECT
u.*
FROM
urls u
( SELECT #nr:=4, #lasthost:=-1 ) AS tmp
WHERE (
last_run_date IS NULL
OR last_run_date <= date_sub(curdate(), INTERVAL 30 DAY)
)
AND ignore_url != 1
ORDER BY host_id, last_run_date
second
I add to variables nr and lasthost and setup it in the select. Now
i count nr each row and reset it to 1 if the host_id is change. So i get a
list of rows numbert from 1 to n for each host_id
SELECT
#nr:=IF(#lasthost = host_id, #nr+1, 1) AS nr,
u.*,
#lasthost:=IF(#lasthost = host_id, #lasthost, host_id) AS lasthost
FROM
urls u,
( SELECT #nr:=4, #lasthost:=-1 ) AS tmp
WHERE (
last_run_date IS NULL
OR last_run_date <= date_sub(curdate(), INTERVAL 30 DAY)
)
AND ignore_url != 1
ORDER BY host_id, last_run_date
third
i put it this query in a new select so i can join your second table and restrict the result only for rows less 11 and also limit the result to 100
SELECT *
FROM (
SELECT
#nr:=IF(#lasthost = host_id, #nr+1, 1) AS nr,
u.*,
#lasthost:=IF(#lasthost = host_id, #lasthost, host_id) AS lasthost
FROM
urls u,
( SELECT #nr:=4, #lasthost:=-1 ) AS tmp
WHERE (
last_run_date IS NULL
OR last_run_date <= date_sub(curdate(), INTERVAL 30 DAY)
)
AND ignore_url != 1
ORDER BY host_id, last_run_date
) AS t
LEFT JOIN HOSTS USING(host_id)
WHERE t.nr < 11
LIMIT 100;
Thats all
So you need a limited JOIN. Another guess:
SELECT * FROM hosts
LEFT JOIN urls ON
urls.host_id = hosts.host_id
WHERE urls.host_id IN
(SELECT host_id FROM urls
LIMIT 0,10)
LIMIT 0,100
I have a table, in which there are date wise quiz score of different users. I want to load top 5 scorers for every date.
Table sample create statement:
CREATE TABLE `subscriber_score` (
`msisdn` varchar(25) COLLATE utf8_unicode_ci NOT NULL,
`date` date NOT NULL,
`score` int(11) NOT NULL DEFAULT '0',
`total_questions_sent` int(11) NOT NULL DEFAULT '0',
`total_correct_answers` int(11) NOT NULL DEFAULT '0',
`total_wrong_answers` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`msisdn`,`date`),
KEY `fk_subscriber_score_subscriber1` (`msisdn`),
CONSTRAINT `fk_subscriber_score_subscriber1` FOREIGN KEY (`msisdn`) REFERENCES `subscriber` (`msisdn`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Query which I have tried:
SELECT subscriber.msisdn AS msisdn,subscriber.name AS name,subscriber.gender AS gender,tmp2.score AS score,tmp2.date AS winning_date
FROM subscriber,
(SELECT msisdn,tmp.date,tmp.score
FROM subscriber_score,
(SELECT date,MAX(score) AS score
FROM subscriber_score
WHERE date > '2014-10-10' AND date < '2014-11-10' GROUP BY date)
tmp
WHERE subscriber_score.date=tmp.date AND subscriber_score.score=tmp.score)
tmp2
WHERE subscriber.msisdn=tmp2.msisdn ORDER BY winning_date
Actual output: Only one top scorer for every date is shown.
Wanted Output Top 5(or say 10) records for every date are required.
I think you can do this using variables to assign each row a row number, then filter the top 5 for each date.
SELECT s.name AS name,
s.gender AS gender,
s.msisdn,
ss.date,
ss.score
FROM ( SELECT ss.msisdn,
ss.score,
#r:= CASE WHEN ss.Date = #d THEN #r + 1 ELSE 1 END AS RowNum,
#d:= ss.date AS winning_date
FROM subscriber_score AS ss
CROSS JOIN (SELECT #d:= '', #r:= 0) AS v
WHERE ss.date > '2014-10-10'
AND ss.date < '2014-11-10'
ORDER BY ss.Date, ss.Score DESC
) AS ss
INNER JOIN Subscriber AS s
ON s.msisdn = ss.msisdn
WHERE ss.RowNum <= 5;
Example on SQL Fiddle
refer this query its not complete but hope it helps
SELECT SCORE
FROM table
WHERE date='somedate'
ORDER BY SCORE DESC LIMIT 5
select bc.msisdn msisdn,bc.name name,bc.gender gender,ab.score score,ab.date winning_date
(
select msisdn,date,score,
dense_rank() over (partition by date order by score desc) rnk
from subscriber_score
) ab,subscriber bc
where bc.msisdn=ab.msisdn and ab.rnk<=5
order by winning_date ;
This is how you can get solution of your problem in oracle sql.
try below
SELECT subscriber.msisdn AS msisdn,subscriber.name AS name,subscriber.gender AS gender,tmp2.score AS score,tmp2.date AS winning_date
FROM subscriber inner join
(select msisdn,date, score, ROW_NUMBER() OVER(PARTITION BY date ORDER BY score DESC) AS Row
FROM subscriber_score
WHERE date > '2014-10-10' AND date < '2014-11-10' GROUP BY date)
tmp
on subscriber.msisdn=tmp.msisdn and tmp.row<=5
Table
Each row represents a video that was on air at particular time on particular date. There are about 1600 videos per day.
CREATE TABLE `air_video` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`date` DATE NOT NULL,
`time` TIME NOT NULL,
`duration` TIME NOT NULL,
`asset_id` INT(10) UNSIGNED NOT NULL,
`name` VARCHAR(100) NOT NULL,
`status` VARCHAR(100) NULL DEFAULT NULL,
`updated` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE INDEX `date_2` (`date`, `time`),
INDEX `date` (`date`),
INDEX `status` (`status`),
INDEX `asset_id` (`asset_id`)
)
ENGINE=InnoDB
Task
There are two conditions.
Each video must be shown not more than 24 times per day.
Each video must be in rotation no longer than 72 hours.
In rotation means time span between the fist and the last time the video was on air.
So I need to select all videos that violate those conditions, given user-specified date range.
The result must be grouped by day and by asset_id (video id). For example:
date asset_id name dailyCount rotationSpan
2012-04-27 123 whatever_the_name 35 76
2012-04-27 134 whatever_the_name2 39 20
2012-04-28 125 whatever_the_name3 26 43
Query
By now I have written this query:
SELECT
t1.date, t1.asset_id, t1.name,
(SELECT
COUNT(t3.asset_id)
FROM air_video AS t3
WHERE t2.asset_id = t3.asset_id AND t3.date = t1.date
) AS 'dailyCount',
MIN(CONCAT(t2.date, ' ', t2.time)) AS 'firstAir',
MAX(CONCAT(t2.date, ' ', t2.time)) AS 'lastAir',
ROUND(TIMESTAMPDIFF(
MINUTE,
MIN(CONCAT(t2.date, ' ', t2.time)),
MAX(CONCAT(t2.date, ' ', t2.time))
) / 60) as 'rotationSpan'
FROM
air_video AS t1
INNER JOIN
air_video AS t2 ON
t1.asset_id = t2.asset_id
WHERE
t1.status NOT IN ('bumpers', 'clock', 'weather')
AND t1.date BETWEEN '2012-04-01' AND '2012-04-30'
GROUP BY
t1.asset_id, t1.date
HAVING
`rotationSpan` > 72
OR `dailyCount` > 24
ORDER BY
`date` ASC,
`rotationSpan` DESC,
`dailyCount` DESC
Problems
The bigger the range between user specified days - the longer it takes to complete the query (for a month range it takes about 9 sec)
The lastAir timestamp is not the latest time the video was aired on particular date but the latest time it was on air altogether.
If you need to speed up your query you need to remove the select sub query on line 3.
To still have that count you can inner join it again in the from clause with the exact parameters you used initially. This is how it should look:
SELECT
t1.date, t1.asset_id, t1.name,
COUNT(t3.asset_id) AS 'dailyCount',
MIN(CONCAT(t2.date, ' ', t2.time)) AS 'firstAir',
MAX(CONCAT(t2.date, ' ', t2.time)) AS 'lastAir',
ROUND(TIMESTAMPDIFF(
MINUTE,
MIN(CONCAT(t2.date, ' ', t2.time)),
MAX(CONCAT(t2.date, ' ', t2.time))
) / 60) as 'rotationSpan'
FROM
air_video AS t1
INNER JOIN
air_video AS t2 ON
(t1.asset_id = t2.asset_id)
INNER JOIN
air_video AS t3
ON (t2.asset_id = t3.asset_id AND t3.date = t1.date)
WHERE
t1.status NOT IN ('bumpers', 'clock', 'weather')
AND t1.date BETWEEN '2012-04-01' AND '2012-04-30'
GROUP BY
t1.asset_id, t1.date
HAVING
`rotationSpan` > 72
OR `dailyCount` > 24
ORDER BY
`date` ASC,
`rotationSpan` DESC,
`dailyCount` DESC
Since t2 is not bound by date, you are obviously looking at the whole table, instead of the date range.
Edit:
Due to a lot of date bindings the query still ran too slowly. I then took a different approach. I created 3 views (which you obviously can combine into a normal query without the views, but I like the end result query better)
--T1--
CREATE VIEW t1 AS select date,asset_id,name from air_video where (status not in ('bumpers','clock','weather')) group by asset_id,date order by date;
--T2--
CREATE VIEW t2 AS select t1.date,t1.asset_id,t1.name,min(concat(t2.date,' ',t2.time)) AS 'firstAir',max(concat(t2.date,' ',t2.time)) AS 'lastAir',round((timestampdiff(MINUTE,min(concat(t2.date,' ',t2.time)),max(concat(t2.date,' ',t2.time))) / 60),0) AS 'rotationSpan' from (t1 join air_video t2 on((t1.asset_id = t2.asset_id))) group by t1.asset_id,t1.date;
--T3--
CREATE VIEW t3 AS select t2.date,t2.asset_id,t2.name,count(t3.asset_id) AS 'dailyCount',t2.firstAir,t2.lastAir,t2.rotationSpan AS rotationSpan from (t2 join air_video t3 on(((t2.asset_id = t3.asset_id) and (t3.date = t2.date)))) group by t2.asset_id,t2.date;
From there you can then just run the following query:
SELECT
date,
asset_id,
name,
dailyCount,
firstAir,
lastAir,
rotationSpan
FROM
t3
WHERE
date BETWEEN '2012-04-01' AND '2012-04-30'
AND (
rotationSpan > 72
OR
dailyCount > 24
)
ORDER BY
date ASC,
rotationSpan DESC,
dailyCount DESC