I have the following code, which firstly retrieves each unique style from a database and then searches through the products table to find the terms associated with each style and updates the stylerefs table with the styleid and the productid:
$query = "Select id,style,terms from su_styles";
if ($results = $sudb->query($query)) {
while($result = $results->fetch_array(MYSQLI_ASSOC)) {
$id=$result['id'];
$style=$result['style'];
$terms=$result['terms'];
$query= "INSERT IGNORE INTO su_stylerefs (mykey,id)
SELECT mykey,$id FROM su_pref where (match(name) against ('$terms' in Boolean Mode))
ON DUPLICATE KEY UPDATE id=$id, mykey = su_pref.mykey";
$sudb->query($query);
Is there anyway I can rewrite this query into one, rather than looping through the results of the first query? I have 10 other similar queries that need to run every day and some of the tables in the first query could have 100s of records, which mean hundreds of connections to the database, which takes a while.
Thank you in advance
I would fetch all these rows first along with the match() case, and then do an update:
INSERT INTO su_stylerefs (mykey, id)
VALUES ($mykey[0], $id[0]), ($mykey[1] $id[1]), ...
ON DUPLICATE KEY UPDATE id=$id, mykey = su_pref.mykey
2nd solution:
INSERT IGNORE INTO su_stylerefs (mykey,id)
SELECT sp.mykey, ss.id
FROM su_styles AS ss
INNER JOIN su_pref AS sp
ON match(sp.name) against (ss.terms in Boolean Mode)
ON DUPLICATE KEY UPDATE id = VALUES(id), mykey = VALUES(mykey)
This may be a better solution.
Related
I'm trying to replace null values in one table with values from a second table, based on matches from other columns in both tables. While the code does not result in error, it does not stop running, producing an unending "running query" signal. code is here
UPDATE pl_building b
INNER JOIN pl_grt t
ON b.INST = t.inst
SET b.Utuition=t.tuition
WHERE b.UtUITION = 0;
You should not update on join tables.
I am not sure what field you want to update, but your SQL should look like this:
UPDATE pl_building b
SET b.Utuition= (select t.tuition from pl_grt t ON b.INST = t.inst)
WHERE b.UtUITION = 0;
Make sure :
1) You have an index on t.inst table column and maybe also on b.UtUITION
2) Relationship between b.INST = t.inst is unique. Never returns more than 1 row.
I am needing some SQL help. I have a SELECT statement that references several tables and is hanging up in the MySQL database. I would like to know if there is a better way to write this statement so that it runs efficiently and does not hang up the DB? Any help/direction would be appreciated. Thanks.
Here is the code:
Select Max(b.BurID) As BurID
From My.AppTable a,
My.AddressTable c,
My.BurTable b
Where a.AppID = c.AppID
And c.AppID = b.AppID
And (a.Forename = 'Bugs'
And a.Surname = 'Bunny'
And a.DOB = '1936-01-16'
And c.PostcodeAnywhereBuildingNumber = '999'
And c.PostcodeAnywherePostcode = 'SK99 9Q9'
And c.isPrimary = 1
And b.ErrorInd <> 1
And DateDiff(CurDate(), a.ApplicationDate) <= 30)
There is NO mysql error in the log. Sorry.
Pro tip: use explicit JOINs rather than a comma-separated list of tables. It's easier to see the logic you're using to JOIN that way. Rewriting your query to do that gives us this.
select Max(b.BurID) As BurID
From My.AppTable AS a
JOIN My.AddressTable AS c ON a.AppID = c.AppID
JOIN My.BurTable AS b ON c.AppID = b.AppID
WHERE (a.Forename = 'Bugs'
And a.Surname = 'Bunny'
And a.DOB = '1936-01-16'
And c.PostcodeAnywhereBuildingNumber = '999'
And c.PostcodeAnywherePostcode = 'SK99 9Q9'
And c.isPrimary = 1
And b.ErrorInd <> 1
And DateDiff(CurDate(), a.ApplicationDate) <= 30)
Next pro tip: Don't use functions (like DateDiff()) in WHERE clauses, because they defeat using indexes to search. That means you should change the last line of your query to
AND a.ApplicationDate >= CurDate() - INTERVAL 30 DAY
This has the same logic as in your query, but it leaves a naked (and therefore index-searchable) column name in the search expression.
Next, we need to look at your columns to see how you are searching, and cook up appropriate indexes.
Let's start with AppTable. You're screening by specific values of Forename, Surname, and DOB. You're screening by a range of ApplicationDate values. Finally you need AppID to manage your join. So, this compound index should help. Its columns are in the correct order to use a range scan to satisfy your query, and contains the needed results.
CREATE INDEX search1 USING BTREE
ON AppTable
(Forename, Surname, DOB, ApplicationDate, AppID)
Next, we can look at your AddressTable. Similar logic applies. You'll enter this table via the JOINed AppID, and then screen by specific values of three columns. So, try this index
CREATE INDEX search2 USING BTREE
ON AddressTable
(AppID, PostcodeAnywherePostcode, PostcodeAnywhereBuildingNumber, isPrimary)
Finally, we're on to your BurTable. Use similar logic as the other two, and try this index.
CREATE INDEX search3 USING BTREE
ON BurTable
(AppID, ErrorInd, BurID)
This kind of index is called a compound covering index, and can vastly speed up the sort of summary query you have asked about.
currently I use a query like this to update a table called cats from a table called admin cats (the $id is because it's in cake, it's just a number like 1,2,3 etc). As it is I name every field (there are maybe 50) and say it like this:
update cats AS c, admin_cats set c.category=(select category from admin_cats where id=$id), [--removed 50 columns in the middle--] c.overviewImageText8=(select overviewImageText8 from admin_cats where id=$id) where c.id = $id");
I'm updating every column, so is there a way I can just say something like:
UPDATE cats SET * = SELECT * FROM admin_cats WHERE admin_cats.id = $id) WHERE cats.id IN ($id);
Of course I tried that and it doesn't work, but I want to do it where the cats row where the id matches $id will update with values from the admin_cats that has that same id. How can I do this?
For MySQL specifically, use REPLACE
REPLACE cats (<optional columns>)
SELECT <cols>
FROM admin_cats
WHERE admin_cats.id = $id;
Note: This assumes cats has a Primary Key (e.g. cats.id) that the duplicate row error can be caught, and the row replaced.
Note: other databases don't support replace or use other, similar but different syntax.
I need to return a single row with some datas taken from some tables not related each others.
So, for example, my actual queries are these (I done it trought a PHP script) :
$query=mysql_query("SELECT trackid FROM tracklist WHERE usersub='".$_SESSION['nickname']."'",$mydb);
echo mysql_num_rows($query);
$query=mysql_query("SELECT trackid FROM comments WHERE usercom='".$_SESSION['nickname']."'",$mydb);
echo mysql_num_rows($query);
$query=mysql_query("SELECT vote FROM vote WHERE uservote='".$_SESSION['nickname']."'",$mydb);
echo mysql_num_rows($query);
$query = mysql_query("SELECT datereg FROM users WHERE nickname='".$_SESSION['nickname']."'",$mydb);
echo mysql_result($query,0,'datereg');
But this will call the MySql server 4 times.
Whats your suggestion to better this situation?
If the tables are not related then you will have to make 4 seperate calls
If the tables COULD be related by foreign keys then you could join them in some way and possibly cut down your sql calls
Ultimately though if you need all of the data then you'll have to request it from the database
You could use a UNION. And, btw, mysql_result is poor. And FFS don't forget to sanitize your inputs!
<?php
$nickname = mysql_escape_string($_SESSION['nickname']);
$sql = "
SELECT COUNT(trackid) AS n FROM tracklist WHERE usersub='{$nickname}'
UNION
SELECT COUNT(trackid) FROM comments WHERE usercom='{$nickname}'
UNION
SELECT COUNT(vote) FROM vote WHERE uservote='{$nickname}'
UNION
SELECT datereg FROM users WHERE nickname='{$nickname}'
";
$result = mysql_query($sql, $db);
while ($row = mysql_fetch_assoc($result)) {
echo $row['n'];
}
?>
I wouldn't really recommend this as it's a bit of a mess combining "count" values with a date in the same column, but you can do it. It's the direct answer to your question.
Well, you could create a fifth table and use it as an index.
If all the values { trackid, vote, datareg } are integers, the index table could contain three columns - nickname, value, and table. When you add records to one of the other tables, add a corresponding record to the index table.
For example,
INSERT INTO vote (vote, uservote, ...) VALUES (123, 'abc', ...);
INSERT INTO myindex (nickname, nvalue, ntable) VALUES ('abc', 123, 'vote');
(I wouldn't actually store the table name as a string but as a numeric value, but you get the idea)
Then on a query, you just SELECT nvalue, ntable FROM myindex WHERE nickname = 'abc';
You will possibly get more than one row.
I think that this is a lot of work and you are better off sticking with the four original queries.
Have you tried combining the select statement together like
SELECT .. Actually.
Maybe you should normalise your database and set up links between your tables...
Edit :: And i'm not sure how you're preparing yourself against mysql injection, but be careful with where your $_SESSION[] comes from
If all the selects return a single row:
$query=mysql_query("
(SELECT trackid FROM tracklist WHERE usersub='".$_SESSION['nickname']."'") as tracklist,
(SELECT trackid FROM comments WHERE usercom='".$_SESSION['nickname']."'") as trackid,
(SELECT vote FROM vote WHERE uservote='".$_SESSION['nickname']."'") as vote,
(SELECT datereg FROM users WHERE nickname='".$_SESSION['nickname']."'") as datereg
"
I realized that i was using a varchar attribute as a index/key in a query, and that is killing my query performance. I am trying to look in my precienct table and get the integer ID, and then update my record in the household table with the new int FK, placed in a new column. this is the sql i have written thus far. but i am getting a
Error 1093 You can't specify target table 'voterfile_household' for update in FROM clause, and i am not sure how to fix it.
UPDATE voterfile_household
SET
PrecID = (SELECT voterfile_precienct.ID
FROM voterfile_precienct INNER JOIN voterfile_household
WHERE voterfile_precienct.PREC_ID = voterfile_household.Precnum);
Try:
update voterfile_household h, voterfile_precienct p
set h.PrecID = p.ID
where p.PREC_ID = h.Precnum
Take a look at update reference here.
Similarly, you can use inner join syntax as well.
update voterfile_household h inner join voterfile_precienct p on (h.Precnum = p.PREC_id)
set h.PrecID = p.ID
What if the subquery returns more than one result? That's why it doesn't work.
On SQL Server you can get this type of thing to work if the subquery does "SELECT TOP 1 ...", not sure if mysql will also accept it if you add a "limit 1" to the subquery.
I also think this is pretty much a duplicate of this question ("Can I have an inner SELECT inside of an SQL UPDATE?") from earlier today.
Firstly, your index on a varchar isn't always a bad thing, if it is not a key you can shrink how much of the field you index to only index say the first 10 chars or so.
Secondly, it won't let you do this as if it is a set that is returned it could break.