I would like to know how to update a table, with values from another table, by the trick is I need to set a limit because I have thousands of rows to update, and PHPmyadmin can't handle that load. ( I dont have direct access to the server )
My table structure looks like this
wp_postmeta
meta_id,
post_id,
meta_key,
meta_value
wp_map
oldmap, newmap
What I need to do is join the two tables on wp_postmeta.meta_value and wp_map.oldmap, and update the wp_postmeta.meta_value with wp_map.newmap.
Here is my current query, but I need to add a LIMIT of 100 to the query, as I'm splitting the query up into smaller chunks so PHPMyAdmin can process.
UPDATE wp_postmeta
INNER JOIN wp_map
ON wp_map.oldmap = wp_postmeta.meta_value
SET wp_postmeta.meta_value = wp_map.newmap;
I read about creating a subquery, but couldn't find any relevant examples, so if someone could steer me in the right direction or provide a working example it would be greatly appreciated.
You can try it this way
UPDATE wp_postmeta t JOIN
(
SELECT p.meta_id, m.newmap
FROM wp_postmeta p JOIN wp_map m
ON p.meta_value = m.oldmap
ORDER BY p.meta_id
LIMIT 100
) s
ON t.meta_id = s.meta_id
SET t.meta_value = s.newmap;
Here is SQLFiddle demo
sqlFiddle
in this example I am only doing 5 rows, you can change the limit to 0,100 if you'd like
UPDATE wp_postmeta,
(SELECT oldmap,newmap
FROM wp_map
ORDER BY oldmap,newmap limit 0,5)as wp_map
SET wp_postmeta.meta_value = wp_map.newmap
WHERE wp_map.oldmap = wp_postmeta.meta_value
It's rather unconventional , but it should help you in a pinch
SET #tmp_c = 0;
UPDATE
table1
INNER JOIN table2 ON table1.id = table2.id
INNER JOIN table3 ON table2.id = table3.id
SET
table1.remove_date = NOW() - INTERVAL 5 MONTH
WHERE
table1.active = 1
AND
(IF (table1.active = 1, #tmp_c := #tmp_c + 1 , 0 )) //This is to only increment the counter if the whole where matches
AND
#tmp_c <= 15 //Start ignoring row items as soon as it exceeds the limit amount
Related
He everyone,
I would like to get all records from a table that are changed, but also if one of its child tables has changed.
I have a working query that does this using subqueries but it takes very long (2 minutes) to complete.
Is there any other way to accomplish this that runs faster?
Thanks!
Wouter
SELECT
wp_posts.id AS order_id,
br_order_archive.id AS archive_id,
wp_posts.post_date_gmt AS date,
wp_posts.post_modified_gmt AS lastmod
FROM
wp_posts
LEFT JOIN
br_order_archive ON br_order_archive.order_id = wp_posts.id
WHERE
wp_posts.post_type = "shop_order"
AND
wp_posts.post_status = "wc-completed"
AND (
br_order_archive.order_id IS NULL
OR wp_posts.post_modified_gmt != br_order_archive.lastmod
OR br_order_archive.lastmod_licenses != (
select max(wp_woocommerce_software_licenses.created)
from wp_woocommerce_software_licenses
where wp_posts.id = wp_woocommerce_software_licenses.order_id
)
OR br_order_archive.lastmod_activations != (
select max(wp_woocommerce_software_activations.activation_time)
from wp_woocommerce_software_activations
LEFT JOIN wp_woocommerce_software_licenses ON wp_woocommerce_software_activations.key_id = wp_woocommerce_software_licenses.key_id
where wp_posts.id = wp_woocommerce_software_licenses.order_id
)
)
ORDER BY
wp_posts.ID desc
In the absence of the necessary debugging information, let's start with this bit....
select max(a.activation_time)
from wp_woocommerce_software_activations a
LEFT
JOIN wp_woocommerce_software_licenses l
ON a.key_id = l.key_id
where p.id = l.order_id
A LEFT JOIN on a table from which you select no columns is a strange thing, as is the correlation between the LEFT JOINed table and the posts table (p). Normally, a LEFT JOINed table would not have an equals operator in a WHERE clause. For my own sanity, I would start by rewriting this as an uncorrelated subquery, regardless of the impact on performance.
I have a very large table (~1 000 000 rows) and complicated query with unions, joins and where statements (user can select different ORDER BY columns and directions). I need to get a row count for pagination. If I run query without counting rows it completes very fast. How can I implement pagination in fastest way?
I tried to use EXPLAIN SELECT and SHOW TABLE STATUS to get approximate row count, but it is very different from real row count.
My query is like this one (simplyfied):
SELECT * FROM (
(
SELECT * FROM table_1
LEFT JOIN `table_a` ON table_1.record_id = table_a.id
LEFT JOIN `table_b` ON table_a.id = table_b.record_id
WHERE table_1.a > 10 AND table_a.b < 500 AND table_b.c = 1
ORDER BY x ASC
LIMIT 0, 10
)
UNION
(
SELECT * FROM table_2
LEFT JOIN `table_a` ON table_2.record_id = table_a.id
LEFT JOIN `table_b` ON table_a.id = table_b.record_id
WHERE table_2.d < 10 AND table_a.e > 500 AND table_b.f = 1
ORDER BY x ASC
LIMIT 0, 10
)
) tbl ORDER BY x ASC LIMIT 0, 10
Query result without limiting is about ~100 000 rows, how can I get this approximate count in fastest way?
My production query example is like this one:
SELECT SQL_CALC_FOUND_ROWS * FROM (
(
SELECT
articles_log.id AS log_id, articles_log.source_table,
articles_log.record_id AS id, articles_log.dat AS view_dat,
articles_log.lang AS view_lang, '1' AS view_count, '1' AS unique_view_count,
articles_log.user_agent, articles_log.ref, articles_log.ip,
articles_log.ses_id, articles_log.bot, articles_log.source_type, articles_log.link,
articles_log.user_country, articles_log.user_platform,
articles_log.user_os, articles_log.user_browser,
`contents`.dat AS source_dat, `contents_trans`.header, `contents_trans`.custom_text
FROM articles_log
INNER JOIN `contents` ON articles_log.record_id = `contents`.id
AND articles_log.source_table = 'contents'
INNER JOIN `contents_trans` ON `contents`.id = `contents_trans`.record_id
AND `contents_trans`.lang='lv'
WHERE articles_log.dat > 0
AND articles_log.dat >= 1488319200
AND articles_log.dat <= 1489355999
AND articles_log.bot = '0'
AND (articles_log.record_id NOT LIKE '%\_404' AND articles_log.record_id <> '404'
OR articles_log.source_table <> 'contents')
)
UNION
(
SELECT
articles_log.id AS log_id, articles_log.source_table,
articles_log.record_id AS id, articles_log.dat AS view_dat,
articles_log.lang AS view_lang, '1' AS view_count, '1' AS unique_view_count,
articles_log.user_agent, articles_log.ref, articles_log.ip,
articles_log.ses_id, articles_log.bot,
articles_log.source_type, articles_log.link,
articles_log.user_country, articles_log.user_platform,
articles_log.user_os, articles_log.user_browser,
`news`.dat AS source_dat, `news_trans`.header, `news_trans`.custom_text
FROM articles_log
INNER JOIN `news` ON articles_log.record_id = `news`.id
AND articles_log.source_table = 'news'
INNER JOIN `news_trans` ON `news`.id = `news_trans`.record_id
AND `news_trans`.lang='lv'
WHERE articles_log.dat > 0
AND articles_log.dat >= 1488319200
AND articles_log.dat <= 1489355999
AND articles_log.bot = '0'
AND (articles_log.record_id NOT LIKE '%\_404' AND articles_log.record_id <> '404'
OR articles_log.source_table <> 'contents')
)
) tbl ORDER BY view_dat ASC LIMIT 0, 10
Many thanks!
If you can use UNION ALL instead of UNION (which is a shortcut for UNION DISTINCT) - In other words - If you don't need to remove duplicates you can try to add the counts of the two subqueries:
SELECT
(
SELECT COUNT(*) FROM table_1
LEFT JOIN `table_a` ON table_1.record_id = table_a.id
LEFT JOIN `table_b` ON table_a.id = table_b.record_id
WHERE table_1.a > 10 AND table_a.b < 500 AND table_b.c = 1
)
+
(
SELECT COUNT(*) FROM table_2
LEFT JOIN `table_a` ON table_2.record_id = table_a.id
LEFT JOIN `table_b` ON table_a.id = table_b.record_id
WHERE table_2.d < 10 AND table_a.e > 500 AND table_b.f = 1
)
AS cnt
Without ORDER BY and without UNION the engine might not need to create a huge temp table.
Update
For your original query try the following:
Select only count(*).
Remove OR articles_log.source_table <> 'contents' from first part (contents) since we know it's never true.
Remove AND (articles_log.record_id NOT LIKE '%\_404' AND articles_log.record_id <> '404' OR articles_log.source_table <> 'contents') from second part (news) since we know it's allways true because OR articles_log.source_table <> 'contents' is allways true.
Remove the joins with contents and news. You can join the *_trans tables directly using record_id
Remove articles_log.dat > 0 since it's redundant with articles_log.dat >= 1488319200
The resulting query:
SELECT (
SELECT COUNT(*)
FROM articles_log
INNER JOIN `contents_trans`
ON `contents_trans`.record_id = articles_log.record_id
AND `contents_trans`.lang='lv'
WHERE articles_log.bot = '0'
AND articles_log.dat >= 1488319200
AND articles_log.dat <= 1489355999
AND articles_log.record_id NOT LIKE '%\_404'
AND articles_log.record_id <> '404'
) + (
SELECT COUNT(*)
FROM articles_log
INNER JOIN `news_trans`
ON `news_trans`.record_id = articles_log.record_id
AND `news_trans`.lang='lv'
WHERE articles_log.bot = '0'
AND articles_log.dat >= 1488319200
AND articles_log.dat <= 1489355999
) AS cnt
Try the following index combinations:
articles_log(bot, dat, record_id)
contents_trans(lang, record_id)
news_trans(lang, record_id)
or
contents_trans(lang, record_id)
news_trans(lang, record_id)
articles_log(record_id, bot, dat)
It depends on the data, which combination ist the better one.
I might be wrong on one ore more points, since i don't know your data and business logic. If so, try to adjust the other.
You can get the calculation when you run the query using SQL_CALC_FOUND_ROWS as explained in the documentation:
select SQL_CALC_FOUND_ROWS *
. . .
And then running:
select FOUND_ROWS()
However, the first run needs to generate all the data, so you are going to get up to 20 possible rows -- I don't think it respects LIMIT in subqueries.
Given the structure of your query and you want to do, I would think first about optimizing the query. For instance, is UNION really needed (it incurs overhead for removing duplicates)? As pointed out in a comment, your joins are really inner joins disguised as outer joins. Indexes might improve performance.
You might want to ask another question, providing sample data and desired results to get advice on such issues.
For the longest time I couldn't figure out why an incorrect COUNT(*) values were being returned. After incrementally removing parts of my query I finally realized that joining tables were the reason behind the incorrect values.
This is the query I'm working with:
SELECT `profiles`.`logo` AS logo,
`companies`.`company_name`,
`companies`.`url_slug`,
count(*)
FROM (`companies`)
JOIN `users` ON `users`.`id` = `companies`.`user_id`
JOIN `categories` ON `categories`.`company_id` = `companies`.`id`
JOIN `products` ON `products`.`company_id` = `companies`.`id`
JOIN `profiles` ON `profiles`.`company_id` = `companies`.`id`
WHERE `users`.`last_login` IS NOT NULL
AND `categories`.`category_id` = '3'
AND `products`.`active` = 1
AND `products`.`xmp_1` = 1
AND `products`.`xmp_2` = 1
AND `profiles`.`field_a` = 1
GROUP BY `companies`.`id`
Running this in my SQL program returns 28 rows, but the COUNT(*) row returns something like 1400. I'm not sure where to head from here. I need a column returned that returns the 28 instead of 1400.
SQL Fiddle with sample data: http://sqlfiddle.com/#!2/97d2d/9
There's no way to do this with a simple query. Chris Hayes shows a way to do this with sub-queries and there are a variety of ways to do that.
The reason is that aggregation functions (like COUNT) only work on the GROUP that a row represents... in this case, each row represents the set of results that have the same company id. There's no way for field in a row to show aggregation information that extends beyond its content, in a simple query.
With subqueries, you can generate the first table, and then aggregate over that. Or, you could just use the fact that the total number of rows returned is included as metadata on the data that is sent back to whichever client submitted the query in the first place, thereby removing the need to include it in the data.
The following query works:
SELECT *, COUNT(*) FROM (
SELECT `profiles`.`logo` AS logo,
`companies`.`company_name`,
`companies`.`url_slug`
FROM (`companies`)
JOIN `users` ON `users`.`id` = `companies`.`user_id`
JOIN `categories` ON `categories`.`company_id` = `companies`.`id`
JOIN `products` ON `products`.`company_id` = `companies`.`id`
JOIN `profiles` ON `profiles`.`company_id` = `companies`.`id`
WHERE `users`.`last_login` IS NOT NULL
#AND `categories`.`category_id` = '3'
AND `products`.`active` = 1
#AND `products`.`xmp_1` = 1
#AND `products`.`xmp_2` = 1
AND `profiles`.`field_a` = 1
GROUP BY `companies`.`id`
) AS stuff
I think somehow your original COUNT(*) is looking at pre-join-condition or pre-grouping amounts.
Everything in the following query results in one line for each invBlueprintTypes row with the correct information. But I'm trying to add something to it. See below the codeblock.
Select
blueprintType.typeID,
blueprintType.typeName Blueprint,
productType.typeID,
productType.typeName Item,
productType.portionSize,
blueprintType.basePrice * 0.9 As bpoPrice,
productGroup.groupName ItemGroup,
productCategory.categoryName ItemCategory,
blueprints.productionTime,
blueprints.techLevel,
blueprints.researchProductivityTime,
blueprints.researchMaterialTime,
blueprints.researchCopyTime,
blueprints.researchTechTime,
blueprints.productivityModifier,
blueprints.materialModifier,
blueprints.wasteFactor,
blueprints.maxProductionLimit,
blueprints.blueprintTypeID
From
invBlueprintTypes As blueprints
Inner Join invTypes As blueprintType On blueprints.blueprintTypeID = blueprintType.typeID
Inner Join invTypes As productType On blueprints.productTypeID = productType.typeID
Inner Join invGroups As productGroup On productType.groupID = productGroup.groupID
Inner Join invCategories As productCategory On productGroup.categoryID = productCategory.categoryID
Where
blueprints.techLevel = 1 And
blueprintType.published = 1 And
productType.marketGroupID Is Not Null And
blueprintType.basePrice > 0
So what I need to get in here is the following table with the columns below it so I can use the values timestamp and sort the entire result by profitHour
tablename: invBlueprintTypesPrices
columns: blueprintTypeID, timestamp, profitHour
I need this information with the following select in mind. Using a select to show my intention of the JOIN/in-query select or whatever that can do this.
SELECT * FROM invBlueprintTypesPrices
WHERE blueprintTypeID = blueprintType.typeID
ORDER BY timestamp DESC LIMIT 1
And I need the main row from table invBlueprintTypes to still show even if there is no result from the invBlueprintTypesPrices. The LIMIT 1 is because I want the newest row possible, but deleting the older data is not a option since history is needed.
If I've understood correctly I think I need a subquery select, but how to do that? I've tired adding the exact query that is above with a AS blueprintPrices after the query's closing ), but did not work with a error with the
WHERE blueprintTypeID = blueprintType.typeID
part being the focus of the error. I have no idea why. Anyone who can solve this?
You'll need to use a LEFT JOIN to check for NULL values in invBlueprintTypesPrices. To mimic the LIMIT 1 per TypeId, you can use the MAX() or to truly make sure you only return a single record, use a row number -- this depends on whether you can have multiple max time stamps for each type id. Assuming not, then this should be close:
Select
...
From
invBlueprintTypes As blueprints
Inner Join invTypes As blueprintType On blueprints.blueprintTypeID = blueprintType.typeID
Inner Join invTypes As productType On blueprints.productTypeID = productType.typeID
Inner Join invGroups As productGroup On productType.groupID = productGroup.groupID
Inner Join invCategories As productCategory On productGroup.categoryID = productCategory.categoryID
Left Join (
SELECT MAX(TimeStamp) MaxTime, TypeId
FROM invBlueprintTypesPrices
GROUP BY TypeId
) blueprintTypePrice On blueprints.blueprintTypeID = blueprintTypePrice.typeID
Left Join invBlueprintTypesPrices blueprintTypePrices On
blueprintTypePrice.TypeId = blueprintTypePrices.TypeId AND
blueprintTypePrice.MaxTime = blueprintTypePrices.TimeStamp
Where
blueprints.techLevel = 1 And
blueprintType.published = 1 And
productType.marketGroupID Is Not Null And
blueprintType.basePrice > 0
Order By
blueprintTypePrices.profitHour
Assuming you might have the same max time stamp with 2 different records, replace the 2 left joins above with something similar to this getting the row number:
Left Join (
SELECT #rn:=IF(#prevTypeId=TypeId,#rn+1,1) rn,
TimeStamp,
TypeId,
profitHour,
#prevTypeId:=TypeId
FROM (SELECT *
FROM invBlueprintTypesPrices
ORDER BY TypeId, TimeStamp DESC) t
JOIN (SELECT #rn:=0) t2
) blueprintTypePrices On blueprints.blueprintTypeID = blueprintTypePrices.typeID AND blueprintTypePrices.rn=1
You don't say where you are putting the subquery. If in the select clause, then you have a problem because you are returning more than one value.
You can't put this into the from clause directly, because you have a correlated subquery (not allowed).
Instead, you can put it in like this:
from . . .
(select *
from invBLueprintTypesPrices ibptp
where ibtp.timestamp = (select ibptp2.timestamp
from invBLueprintTypesPrices ibptp2
where ibptp.blueprintTypeId = ibptp2.blueprintTypeId
order by timestamp desc
limit 1
)
) ibptp
on ibptp.blueprintTypeId = blueprintType.TypeID
This identifies the most recent records for all the blueprintTypeids in the subquery. It then joins in the one that matches.
I am trying to create an update query and making little progress in getting the right syntax.
The following query is working:
SELECT t.Index1, t.Index2, COUNT( m.EventType )
FROM Table t
LEFT JOIN MEvents m ON
(m.Index1 = t.Index1 AND
m.Index2 = t.Index2 AND
(m.EventType = 'A' OR m.EventType = 'B')
)
WHERE (t.SpecialEventCount IS NULL)
GROUP BY t.Index1, t.Index2
It creates a list of triplets Index1,Index2,EventCounts.
It only does this for case where t.SpecialEventCount is NULL. The update query I am trying to write should set this SpecialEventCount to that count, i.e. COUNT(m.EventType) in the query above. This number could be 0 or any positive number (hence the left join). Index1 and Index2 together are unique in Table t and they are used to identify events in MEvent.
How do I have to modify the select query to become an update query? I.e. something like
UPDATE Table SET SpecialEventCount=COUNT(m.EventType).....
but I am confused what to put where and have failed with numerous different guesses.
I take it that (Index1, Index2) is a unique key on Table, otherwise I would expect the reference to t.SpecialEventCount to result in an error.
Edited query to use subquery as it didn't work using GROUP BY
UPDATE
Table AS t
LEFT JOIN (
SELECT
Index1,
Index2,
COUNT(EventType) AS NumEvents
FROM
MEvents
WHERE
EventType = 'A' OR EventType = 'B'
GROUP BY
Index1,
Index2
) AS m ON
m.Index1 = t.Index1 AND
m.Index2 = t.Index2
SET
t.SpecialEventCount = m.NumEvents
WHERE
t.SpecialEventCount IS NULL
Doing a left join with a subquery will generate a giant
temporary table in-memory that will have no indexes.
For updates, try avoiding joins and using correlated
subqueries instead:
UPDATE
Table AS t
SET
t.SpecialEventCount = (
SELECT COUNT(m.EventType)
FROM MEvents m
WHERE m.EventType in ('A','B')
AND m.Index1 = t.Index1
AND m.Index2 = t.Index2
)
WHERE
t.SpecialEventCount IS NULL
Do some profiling, but this can be significantly faster in some cases.
my example
update card_crowd as cardCrowd
LEFT JOIN
(
select cc.id , count(1) as num
from card_crowd cc LEFT JOIN
card_crowd_r ccr on cc.id = ccr.crowd_id
group by cc.id
) as tt
on cardCrowd.id = tt.id
set cardCrowd.join_num = tt.num;