I'm trying to update a column in visitors. I'm also using a sub select statement for the SET part of the update query.
UPDATE
visitors AS v
SET
v.IsFirstVisit = (SELECT COUNT(*) FROM visitors AS v2 WHERE ..... LIMIT 1)
However, mySQL returns this error message
#1093 - You can't specify target table 'v' for update in FROM clause
I have no clue why I can't access the 'v' object within the inner select statement. I also don't want to use multiple statements as this would cause a performance issue.
Question: How can I use the 'v' object within the inner select?
Update:
This is the entire query
UPDATE
visitors AS v
SET
IsFirstVisit = (SELECT Count(*) FROM visitors AS v2 WHERE v2.VisitorId < v.VisitorId AND v2.IP = v.IP AND v2.DateTime > v.DateTime [TODO:SUBTRACT30MINUTES] LIMIT 1)
WHERE
VisitorId = "991"
i guess you looking for this
UPDATE
visitors
SET
IsFirstVisit = (SELECT COUNT(*) FROM visitors WHERE ..... LIMIT 1)
edit:
try this
UPDATE
visitors
SET
IsFirstVisit = (SELECT Count(*) FROM visitors v2 inner join visitors v
ON v.VisitorId = v2.VisitorId WHERE v2.IP = v.IP AND v2.DateTime > v.DateTime AND v2.VisitorId < v.VisitorId [TODO:SUBTRACT30MINUTES] LIMIT 1)
WHERE
VisitorId = "991"
The inner join in UPDATE statement won't be a bad idea.
UPDATE
visitors inner join (SELECT COUNT(*) as test FROM visitors v) as v
SET
isfistvisit = v.test;
Another workaround which Im not a big fan of it.
update visitors
set isfistvisit = (
select count(*) from (
select count(*) from visitors
) as x
)
Demo
Related
So I created this query
SELECT a.email
, a.password
, a.server
, a.status
, a.step
, b.*
FROM mail a
JOIN automation_logs b
ON b.accountid = a.accountid
WHERE a.status <0
ORDER
BY b.created DESC;
What it's used for is essentially I'm trying to get debug information from our log table for email accounts that are errored out on our api. The automation_logs table essentially is text dump of error messages that were produced by our API.
This query gives me what I want but gives me too much. I only want the last five most recent (read: created field is a timestamp) error messages for any of the accounts as some of these accounts have existed for years and old errors have been fixed and aren't necessary to know what's broken and just pad the results.
Is this possible with a single query?
Following the advice listed here: Thank you Marshal Tigerus
I was able to get it to work with the following sql:
SET #currcount = NULL, #currvalue = NULL;
SELECT a.email,a.password,a.serverid,a.status,a.step,b.message,b.created FROM mail as a JOIN (
SELECT * FROM (
SELECT
*,
#currcount := IF(#currvalue = accountid, #currcount + 1, 1) AS num,
#currvalue := accountid AS cur_id
FROM automation_logs
WHERE message NOT LIKE ""
ORDER BY accountid, created DESC
) as limitedresults WHERE num <= 5
) as b ON a.accountid = b.accountid WHERE a.status < 0 ORDER BY b.created DESC
I have this nice and neat way of loading posts for my blog website to fit specified page:
$end = $count - ($page * $ppp); //count = select max(id) from art;
$start = $count- ($page * $ppp) - ($ppp-1);
$nxtpage = $page +1; //this is set beforehand in case no posts exists
$prvpage = $page == 0 ? 0 : $page -1;
$sql = "SELECT
a.id AS id,
a.nazwa AS nazwa,
a.data AS data,
a.wstep AS wstep,
a.imgs AS imgs,
a.zdj AS zdj,
GROUP_CONCAT(t.nazwa) all_tags
FROM
art a INNER JOIN tagart ta ON a.id = ta.id INNER JOIN tags t ON t.idt = ta.idt
WHERE a.id BETWEEN $start AND $end
GROUP BY a.id
ORDER BY a.id desc";
This way I can load only a specified numer of posts depended by blogs page (pagination).
There is a pretty big problem with it tho.
Lets say my client make a mistake like writing BLACK PPL somewhere in one article half a year ago, and now he has to delete it.
Or even better, has to delete about 10 posts from it. When middle posts are deleted, the whole alrorithm gets messed up, because it scans posts based it their ID.
So my question here for you is what better way of picking the posts I could use, that would always get the correct order of the posts?
It looks like you're trying to implement your own way of doing LIMIT, which is a MySQL feature that handles pagination. Instead of manually defining your start and end ID's, you should be looking to order your posts and then only fetching the next X posts, no matter what their ID's are. Here's how you would do that
$start = ($page - 1) * $ppp;
$sql = "SELECT
a.id AS id,
a.nazwa AS nazwa,
a.data AS data,
a.wstep AS wstep,
a.imgs AS imgs,
a.zdj AS zdj,
GROUP_CONCAT(t.nazwa) all_tags
FROM
art a INNER JOIN tagart ta ON a.id = ta.id INNER JOIN tags t ON t.idt = ta.idt
GROUP BY a.id
ORDER BY a.id DESC
LIMIT $start,$ppp";
LIMIT is used as either
LIMIT 5 #Fetch first 5 items
or
LIMIT 5,10 #Starting from the 5th item, fetch the next 10 items
Instead of using max(id) to determine the number of posts, use count(id) or count(*) to actually count them. If a post gets deleted, the count can take that into account.
In the select query use limit to select the range of posts to show.
As you already figured out, you should not be relying on MAX(id) to count the number of records.
I would just use a separate count query to get the count. It's simple and relatively inexpensive:
SELECT COUNT (id) FROM art
And as others have already mentioned, use LIMIT to paginate instead of limiting by id.
SELECT * FROM `surveys` INNER JOIN `surveyusers` ON `surveyusers`.`survID` = `surveys`.`survID` WHERE `surveyusers`.`hasWon` = 0 ORDER BY RAND();
UPDATE surveys SET winner = email;
This was a school assignment that required us to select and update the survey winner.
I use PHP to manage and show surveys, I want the SQL to pick the winner. PHP sets survID
I want to pick a random winner and set the winner coloumn in surveys table to the email in the surveyusers table. But I really have a lot of trouble with it.
Updates can take the same syntax as a Select.
You can combine these two queries into one.
UPDATE `surveys` `s` SET `s`.`winner` = (
SELECT email FROM `surveyusers`
WHERE `surveyusers`.`hasWon` = 0
AND `survID` = 1
ORDER BY RAND()
LIMIT 1
) WHERE `survID` = 1;
The survID you would have to set from PHP of course.
You can nest a SELECT query within your UPDATE query:
UPDATE surveys SET winner = (
SELECT `surveyusers`.email FROM `surveys`
INNER JOIN `surveyusers` ON `surveyusers`.`survID` = `surveys`.`survID`
WHERE `surveyusers`.`hasWon` = 0 ORDER BY RAND() LIMIT 1
) WHERE `survID` = xxx
where xxx is the survey to update.
Can anyone tell me how to make this query faster?
$session_id = '000000000015';
$start = 0;
$finish = 30;
try {
$stmt = $conn->prepare("SELECT TOPUSERS.ID, TOPUSERS.USERNAME, TOPUSERS.NAME, TOPUSERS.NAME2, TOPUSERS.PHOTO, TOPUSERS.FB_USERID, TOPUSERS.IMAGE_TYPE, TOPUSERS.TW_USERID, TOPUSERS.TW_PHOTO,
COALESCE((SELECT COUNT(USERS_BUCKETS.ID) FROM USERS_BUCKETS WHERE USERS_BUCKETS.USERID=TOPUSERS.ID),0) AS NUM_ALL,
COALESCE((SELECT SUM(CASE WHEN USERS_BUCKETS.STATUS='Completed' THEN 1 ELSE 0 END) FROM USERS_BUCKETS WHERE USERS_BUCKETS.USERID=TOPUSERS.ID),0) AS NUM_DONE,
COALESCE((SELECT COUNT(USERS_LIKES.ID) FROM USERS_LIKES WHERE USERS_LIKES.USERID=TOPUSERS.ID),0) AS NUM_LIKES,
(SELECT USERS_BUCKETS.BUCKETID FROM USERS_BUCKETS WHERE USERS_BUCKETS.USERID=TOPUSERS.ID ORDER BY USERS_BUCKETS.DATE_MODIFIED DESC LIMIT 1) AS RECENT_BUCKET,
(SELECT BUCKETS_NEW.BUCKET_NAME FROM BUCKETS_NEW WHERE BUCKETS_NEW.ID=RECENT_BUCKET) AS REC,
COALESCE((SELECT COUNT(ID) FROM FOLLOW WHERE FOLLOW.USER_ID=TOPUSERS.ID),0) AS FOLLOWING,
COALESCE((SELECT COUNT(ID) FROM FOLLOW WHERE FOLLOW.FOLLOW_ID=TOPUSERS.ID),0) AS FOLLOWERS,
(SELECT IF(TOPUSERS.NAME = '',0,1) + IF(TOPUSERS.BIO = '',0,1) + IF(TOPUSERS.LOCATION = '',0,1) + IF(TOPUSERS.BIRTHDAY = '0000-00-00',0,1) + IF(TOPUSERS.GENDER = '',0,1)) as COMPLETENESS,
CASE WHEN ? IN (SELECT USER_ID FROM FOLLOW WHERE FOLLOW_ID = TOPUSERS.ID) THEN 'Yes' ELSE 'No' END AS DO_I_FOLLOW_HIM
FROM TOPUSERS
LEFT JOIN FOLLOW ON TOPUSERS.ID = FOLLOW.FOLLOW_ID
LEFT JOIN USERS_BUCKETS ON USERS_BUCKETS.USERID=TOPUSERS.ID
LEFT JOIN BUCKETS_NEW ON BUCKETS_NEW.ID=USERS_BUCKETS.BUCKETID
WHERE NOT TOPUSERS.ID = ?
GROUP BY TOPUSERS.ID ORDER BY TOPUSERS.RANDOM, TOPUSERS.USERNAME LIMIT $start, $finish");
When I run this in a browser it takes about 7 seconds to load. Without a few lines (the COALESCE in the middle, the two SELECTS above and the line below them) the time is reduced to 3-4 seconds.
The result of the query is a list of people with names, profile picture and some data.
TL,DR: you need to rewrite the query.
You need to rewrite your query to make it more efficient. I had to rewrite a similar query at work last week and here is what I have done.
The structure of your query should look like this to be efficient:
select ...
...
from ...
join ...
where ...
what you have now is something like:
select ...
inner select
inner select
from ...
join ...
where ...
That's the inner selects that kill your query. You need to find a way to move the inner select into the from section. Especially that you already query the tables.
What you need to understand is that your inner selects run for every records you have. So if you have 10 records, it would be alright (speed wise). But with hundred or thousand of records, it would be very slow.
If you want more information on your query run it with the explain keyword in from of it.
I'm working with the join plus union plus group by query, and I developed a query something like mentioned below:
SELECT *
FROM (
(SELECT countries_listing.id,
countries_listing.country,
1 AS is_country
FROM countries_listing
LEFT JOIN product_prices ON (product_prices.country_id = countries_listing.id)
WHERE countries_listing.status = 'Yes'
AND product_prices.product_id = '3521')
UNION
(SELECT countries_listing.id,
countries_listing.country,
0 AS is_country
FROM countries_listing
WHERE countries_listing.id NOT IN
(SELECT country_id
FROM product_prices
WHERE product_id='3521')
AND countries_listing.status='Yes')) AS partss
GROUP BY id
ORDER BY country
And I just realised that this query is taking a lot of time to load results, almost 8 seconds.
I was wondering if there is the possibility to optimize this query to the fastest one?
If I understand the logic correctly, you just want to add a flag for the country as to whether or not there is a price for a given product. I think you can use an exists clause to get what you want:
SELECT cl.id, cl.country,
(exists (SELECT 1
FROM product_prices pp
WHERE pp.country_id = cl.id AND
pp.product_id = '3521'
)
) as is_country
FROM countries_listing cl
WHERE cl.status = 'Yes'
ORDER BY country;
For performance, you want two indexes: countries_listing(status, country) and
product_prices(country_id, product_id)`.
Depending on how often it is executed, prepared statements could help. See PDO for more information.