I have a requirement to update the first 8 rows in a table each time the page refreshes.
The way I have tried to approach this is using the query below but it updates all the rows in the table.
Can anyone see where I am going wrong or is there a better way to do this.
$query_conf_update = "
UPDATE ConfBookings2017
Set Screen1
WHERE HotelID ='".$HotelID."'
AND RecordID IN (
SELECT RecordID FROM (
SELECT RecordID
FROM ConfBookings2017
WHERE HotelID ='".$HotelID."'
ORDER
BY RoomFromTime DESC
LIMIT 0, 8
) tmp
)";
Single-table UPDATE allows ORDER BY and LIMIT, so subquery is excess:
UPDATE ConfBookings2017
Set Screen1 = 'new value' /* or parameter placeholder */
WHERE HotelID ='".$HotelID."' /* recommendation - convert to parameter */
/* AND another conditions, for example, Screen1 != 'new value' */
ORDER BY RoomFromTime DESC
LIMIT 8;
Related
I want to improve my current query. So I have this table called Incomes. Where I have a sourceId varchar field. I have a single SELECT for the fields I need, but I needed to add an extra field called isFirstTime to represent if it was the first time on the row on what that sourceId was used. This is my current query:
SELECT DISTINCT
`income`.*,
CASE WHEN (
SELECT
`income2`.id
FROM
`income` as `income2`
WHERE
`income2`."sourceId" = `income`."sourceId"
ORDER BY
`income2`.created asc
LIMIT 1
) = `income`.id THEN true ELSE false END
as isFirstIncome
FROM
`income` as `income`
WHERE `income`.incomeType IN ('passive', 'active') AND `income`.status = 'paid'
ORDER BY `income`.created desc
LIMIT 50
The query works but slows down if I keep increasing the LIMIT or OFFSET. Any suggestions?
UPDATE 1:
Added WHERE statements used on the original query
UPDATE 2:
MYSQL version 5.7.22
You can achieve it using Ordered Analytical Function.
You can use ROW_NUMBER or RANK to get the desired result.
Below query will give the desired output.
SELECT *,
CASE
WHEN Row_number()
OVER(
PARTITION BY sourceid
ORDER BY created ASC) = 1 THEN true
ELSE false
END AS isFirstIncome
FROM income
WHERE incomeType IN ('passive', 'active') AND status = 'paid'
ORDER BY created desc
DB Fiddle: See the result here
My first thought is that isFirstIncome should be an extra column in the table. It should be populated as the data is inserted.
If you don't like that, let's try to optimize the query...
Let's avoid doing the subquery more than 50 times. This requires turning the query inside-out. (It's like "explode-implode", where the query gathers lots of stuff, then sorts it and throws most of the rows away.)
To summarize:
do the least amount of effort to just identify the 5 rows.
JOIN to whatever tables are needed (including itself if appropriate); this is to get any other columns desired (including isFirstIncome).
SELECT i3.*,
( ... using i3 ... ) as isFirstIncome
FROM (
SELECT i1.id, i1.sourceId
FROM `income` AS i1
WHERE i1.incomeType IN ('passive', 'active')
AND i1.status = 'paid'
ORDER BY i1.created DESC
LIMIT 50
) AS i2
JOIN income AS i3 USING(id)
ORDER BY i2.created DESC -- yes, repeated
(I left out the computation of isFirstIncome; it is discussed in other Answers. But note that it will be executed at most 50 times.)
(The aliases -- i1, i2, i3 -- are numbered in the order they will be "used"; this is to assist in following the SQL.)
To assist in performance, add
INDEX(status, incomeType, created, id, sourceId)
It should help with my formulation, but probably not for the other versions. Your version would benefit from
INDEX(sourceId, created, id)
I'm moving a few boolean columns from the 1st row of a generic settings table into the 1st row of a website_settings table across a few MYSQL databases. I've created the new columns in my new table with a default false value.
I have a working query to copy data from the old table:
UPDATE website_settings
SET
dark_mode_enabled = (SELECT dark_mode_enabled FROM settings ORDER BY id LIMIT 1),
header_enabled = (SELECT header_enabled FROM settings ORDER BY id LIMIT 1),
footer_enabled = (SELECT footer_enabled FROM settings ORDER BY id LIMIT 1)
LIMIT 1;
However for my own knowledge, I'm curious if there is a more cleaner way to write this, perhaps without the repetitive select queries to the same table?
You could use a join:
UPDATE website_settings ws CROSS JOIN
(SELECT s.*
FROM settings s
ORDER id DESC
LIMIT 1
) s
SET ws.dark_mode_enabled = s.dark_mode_enabled,
ws.header_enabled = s.header_enabled,
ws.footer_enabled = s.footer_enabled
LIMIT 1;
Here's a solution that does not use JOIN:
INSERT INTO website_settings (id, dark_mode_enabled, header_enabled, footer_enabled)
SELECT id, dark_mode_enabled, header_enabled, footer_enabled
FROM settings ORDER BY id LIMIT 1
ON DUPLICATE KEY UPDATE
dark_mode_enabled = VALUES(dark_mode_enabled),
header_enabled = VALUES(header_enabled),
footer_enabled = VALUES(footer_enabled);
I have a dynamic MySQL-Table with 20 columns and 1 Row where id = 1. The new data will be added to the table as the sencond row where id = 2, And so an. I need to read the newest data row (always the last id). How should I write the query to read the last row?
How should I change this:
$sql = "SELECT id, AbW_L, PuVor_L ,Durchfluss ,... FROM table order by id desc ";
Order by id descending, and limit the results to one row:
SELECT * from table order by id desc limit 1;
You might want to take a look at: http://www.techonthenet.com/sql/order_by.php
It will give you more detailed information on what to do, however based on that one line of code, just change it to:
$sql = "SELECT * FROM `table_name` ORDER BY `id` DESC";
Should work no problem. If you want to add limits, you can do:
$sql = "SELECT * FROM `table_name` ORDER BY `id` DESC LIMIT 10";
I selected 10 as the limit in the example, you can pick whatever number you would like to use.
Query 1:
set #userName = 'harry';
set #previousRegionId = (
select Region as RegionID
from log
where User = #userName
order by stamp desc
limit 1);
select #previousRegionId;
Query 2:
set #previousRegionId = (
select Region as RegionID
from log
where User = 'harry'
order by stamp desc
limit 1);
select #previousRegionId;
I expect the same result for both queries, but the result is different and it is reproduceable!
The log table contains same value in User column for all rows ('harry').
Changed the order by clause. Order by id works "better". Order by timestamp column is suspect ...
I am not 100% sure if this approach will work in all scenarios/aspects.
Assume the following records:
ID Value
=========
1. No
2. No
3. No
4. No
5. No
6. No
7. Yes
8. No
9. No
10. No
Each "page" of my records-display contains 5 records (so page 1 has records 1-5, page 2 has records 6-10, and so on ...). I want to display the page that contains the record with the value of Yes. Keep in mind that I don't really know where this Yes is in the records.
How do I query this?
How about something like this:
/* Work out the page */
SELECT #MatchID = ID
FROM tbl
WHERE Value = 'Yes'
ORDER BY ID ASC
LIMIT 1;
SELECT #Page = CEIL(COUNT(*) / 5)
FROM tbl
WHERE ID <= #MatchID;
/* Select the items on that page */
SET #Offset = (#Page - 1) * 5;
SELECT *
FROM tbl
ORDER BY ID ASC
LIMIT #Offset, 5;
Note: The above doesn't cater for #MatchID not being found.
I'm unsure whether MySQL limits have to be constants, hence if they do you would have to calculate the offset in PHP, or whatever programming language you're using to connect to MySQL. Alternatively, maybe this would work, instead of the last SELECT statement in the above example:
SET #selectSQL = CONCAT('SELECT * FROM tbl ORDER BY ID ASC LIMIT ', #Offset, ', 5');
PREPARE stmt FROM #selectSQL;
EXECUTE stmt;
SELECT ID FROM tbl WHERE Value = "Yes" Order by ID ASC LIMIT 1;
divide the ID through the "records-per-page" (in your case: 5).
Then you get the "page" where the first yes-record exists.
Assuming your ID is Primary Key (auto_increment) and without gaps.
SELECT Value, ID / 5 AS Page WHERE Value = "Yes"
EDIT
SELECT Value, ID DIV 5 + 1 AS Page WHERE Value = "Yes"
Alright, I think I got this now.
select all records on the same page as the record with Value = 'YES'
SET #c:=0;
SELECT tbl.* FROM tbl WHERE CEIL((#c:=#c+1) / 5) = (SELECT CEIL(COUNT(temp_table.id) / 5) FROM tbl temp_table WHERE temp_table.id <= (SELECT temp2.id FROM tbl temp2 WHERE temp2.value = 'YES')) ORDER BY tbl.id ASC;