Replicating a "For Each" loop in a MySQL query - mysql

I've been using MySQL at work, but I'm still a bit of a noob at more advanced queries, and often find myself writing lengthy queries that I feel (or hope) could be significantly shortened.
I recently ran into a situation where I need to create X number of new entries in a table for each entry in another table. I also need to copy a value from each row in the second table into each row I'm inserting into the first.
To be clear, here's pseudocode for what I'm attempting to do:
For each row in APPS
create new row in TOKENS
set (CURRENT)TOKENS.APP_ID = (CURRENT)APPS.APP_ID
Any help is appreciated, even if it boils down to "this isn't possible."
As a note, the tables only share this one field, and I'll be setting other fields statically or via other methods, so simply copying isn't really an option.

You don't need a loop, you can use a single INSERT command to insert all rows at once:
INSERT INTO TOKENS (APP_ID)
SELECT APP_ID
FROM APPS;
If you want to set other values for that row, simply modify the INSERT list and SELECT clause. For example:
INSERT INTO TOKENS (APP_ID, static_value, calculated_value)
SELECT APP_ID, 'something', 'calculated-' + APP_ID
FROM APPS

Related

Joining across 3000+ tables

Ok, strange one here
I have a database for customer data. My customers are businesses with their own customers.
I have 3000 tables (one for each business) with several thousand email addresses in each. Each table is identical, save the name.
I need to find a way to find where emails cross over between businesses (ie appear in multiple tables) and the name of the table that they sit in.
I have tried collating all entries and table names into one table and using a "group by", but the volume of data is too high to run this without our server keeling over...
Does anyone have a suggestion on how to accomplish this without running 3000 sets of joins?
Also, I cannot change the data structure AT ALL.
Thanks
EDIT: In response to those "helpful" restructure comments, not my database, not my system, I only started a couple of months ago to analyse the data
Multiple tables of identical structure almost never makes sense, all it would take is a business field to fix this structure. If at all possible you should fix the structure. If it has been foisted upon you and you cannot change it, you should still be able to work with it.
Select the distinct emails and the table name from each table either UNION ALL or pull them into a new table, then use GROUP BY and HAVING to find emails with multiple tables.
SELECT email
FROM Combined_Table
GROUP BY email
HAVING COUNT(sourc_table) > 1
So, you say you can't change the data structure, but you might be able to provide a compatible upgrade.
Provide a new mega table:
CREATE TABLE business_email (
id_business INT(10) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
PRIMARY KEY id_business, email
) ENGINE = MYISAM;
Myisam engine so you don't have to worry about transactions.
Add a trigger to every single business table to duplicate the email into the new one:
DELIMITER \\
CREATE TRIGGER TRG_COPY_EMAIL_BUSINESS1 AFTER INSERT OR UPDATE ON business1 FOR EACH ROW
BEGIN
INSERT INTO `business_email` (`id_business`, `email`) VALUES (NEW.`id_business`, NEW.`email`) ON DUPLICATE KEY UPDATE `id_business`=NEW.`id_business`;
END;
\\
DELIMITER ;
Your problem is to add it dynamically whenever a new table is created. It shouldn't be a problem since apparently there's already dynamic DDL in your application code.
Copy all existing data to the new table:
INSERT INTO `business_email` (`id_business`, `email`)
SELECT email FROM business1
UNION
SELECT email FROM business2
...
;
COMMIT;
proceed with your query on the new business_email table, that should be greatly simplified:
SELECT `id_business` FROM `business_email`
WHERE
GROUP BY `email`
HAVING COUNT(`email`) > 2;
This query should be easy to cope with. If not, please detail the issue as I don't think properly indexed tables should be a problem even for millions of rows (Which I don't believe is the case since we talk about emails)
The advantage of this solution is that you stay up to date all the time, while you don't change the way your application works. You just add another layer to provide additional business value.

selectively UPDATING old rows when INSERTING new ones into table (trigger issue) in MySQL

I've come across a slight issue when designing a trigger statement in our MySQL DB today:
We have the following relevant tables:
quote (quote_key, date, discount_rate, discount_period, supplier_key)
part (part_key, part_name, ...)
part_quote (pq_key, part_price, fk_quote_key, fk_part_key)
part_approval (papproval_key, is_approved, fk_part_quote_key)
Now for an explanation of the logic:
Every supplier issues quotes for different inputs into a manufacturing process (e.g. parts and components). If the price for a part is about right, they will be approved for manufacturing and can thus be used by our engineers. Since we want to be able to receive quotes from different suppliers for the same parts to enable a comparison, I've tried to model this process by using the part_quote and part_approval table.
On to my problem: If I want to approve a new part_quote, I would like the BOOL flag "is_approved" in all (or the most recent) old quotes to be automatically set to FALSE.
I tried to get this done by issuing a trigger statement:
CREATE TRIGGER update_approval BEFORE INSERT ON part_approval
FOR EACH ROW --- ??
I have some problems selecting the right rows to update:
How do I select the part_key, which will ultimately identify all relevant rows that require updating?
How do I select only the old (or most recent) rows?
I would have loved to include a screener but unfortunately, I do not have 10reps yet :/
Thank you so much in advance,
All the best,
Marius
First I will answer your question as-is to the best of my ability since it is helpful to know more about how to use triggers. Then I will explain why you shouldn't actually be using triggers.
Triggers provide access to two special aliases, which are not otherwise available: OLD and NEW. NEW lets you access the new values and works in update/insert triggers. OLD lets you access the old values and works in update/delete triggers.
For your case you would probably want something like this:
CREATE TRIGGER `update_approval` BEFORE INSERT ON `part_approval`
FOR EACH ROW
UPDATE `table_x` SET `value` = y WHERE `z` = NEW.a;
For more information and some useful examples of triggers, I would suggest reading this: http://dev.mysql.com/doc/refman/5.0/en/trigger-syntax.html
HOWEVER, triggers in MySQL cannot update the same table which they were triggered on, which is what you are looking to do. There is a good question someone had relating to this here: MySQL - Trigger for updating same table after insert and a good answer that you should use a stored procedure in this case.
I'm not sure this will solve my problem entirely, but based on your suggestions I came across a couple of helpful posts, that were able to implement similar updates within a trigger with multiple statements.
CREATE TRIGGER update_approval BEFORE INSERT ON part_approval
FOR EACH ROW BEGIN
DECLARE part_id INT;
SELECT part_key INTO part_id
FROM part p, part_quote pq, part_approval a
WHERE NEW.part_quote_key=pq.part_quote_key AND pq.part_key = p.part_key;
UPDATE part p, part_quote pq, part_approval a
SET a.is_approved=DEFAULT
WHERE pq.part_key=part_id AND a.approval_date<NEW.approval_date;
END;
will only be able to try it out on monday after the DB has been populated.
thanks for the help!

How to insert many related records in one sql file

I want to insert many records in 3 tables, businesses, business_categories (business_id), business_details(business_id), I want to insert many data.
as you can see, the data in business_categories and business_details will need to use the business's id, and when inserting data, I still don't know the business id.
in the past, I was writing a ruby script to do that things, but it's pretty slow. and now I want to write a script to generate sql file directly, it can be the fast way.
I would write this.
insert into businesses(name, ..) values ("blabla", ...)
insert into business_categories(business_id, ..) values(? ..)
insert into business_details(business_id, ..) values(? ..)
Is it possible to set the right value to the ? ?
thanks.
You could save the last inserted id to a variable and set that value in the following inserts.
https://stackoverflow.com/a/17112962/1165441
This answer has some info that is really good.

Insert random number into table upon new record creation

I would like to store random numbers in one MySql table, randomly retrieve one and insert it into another table column each time a new record is created. I want to delete the retrieved number from the random number table as it is used.
The random numbers are 3 digit, there are 900 of them.
I have read several posts here that describe the problems using unique random numbers and triggering their insertion. I want to use this method as it seems to be reliable while generating few problems.
Can anyone here give me an example of a sql query that will accomplish the above? (If sql query is not the recommended way to do this please feel free to recommend a better method.)
Thank you for any help you can give.
I put together the two suggestions here and tried this trigger and query:
CREATE TRIGGER rand_num before
INSERT ON uau3h_users FOR EACH ROW
insert into uau3h_users (member_number)
select random_number from uau3h_rand900
where random_number not in (select member_number from uau3h_users)
order by random_number
limit 1
But it seems that there is already a trigger attached to that table so the new one cause a conflict, things stopped working until I removed it. Any ideas about how accomplish the same using another method?
You are only dealing with 900 records, so performance is not a major issue.
If you are doing a single insert into a table, you can do something like the following:
insert into t(rand)
select rand
from rand900
where rand not in (select rand from t)
order by rand()
limit 1
In other words, you don't have to continually delete from one table and move to the other. You can just choose to insert values that don't already exist. If performance is a concern, then indexes will help in this case.
More than likely you need to take a look into Triggers. You can do some stuff for instance after inserting a record in a table. Refer this link to more details.
http://dev.mysql.com/doc/refman/5.0/en/create-trigger.html

Relative costs of DELETE / INSERT call pair vs an 'if exists' check beforehand?

We're trying to figure out what the relative costs are between a couple of approaches.
We have a web page where people choose to add/keep/remove rows from a table, by marking them with checkboxes. (People can add new entries to the page as well as see existing ones.)
When posted to the web server the page loops over the entries and calls a stored procedure, passing in the state of the checkbox as one of the parameters.
The stored procedure currently calls a delete statement for each entry, followed by an insert if the checkbox is marked. This has the virtue of simplicity.
We're thinking instead of putting some if exists logic in there, to test whether the row is already in the table.
If so and the checkbox is marked, we'd leave it alone. Otherwise we'd insert it. Conversely, if the row isn't in the table and the checkbox is unmarked, we'd skip the delete and insert statements. This minimizes the number of deletes and such but at a cost of more logic.
In terms of load on the database, is one approach generally preferred to the other?
Is there a cost to calling delete statements that don't, in fact, affect any rows, as would be the case when adding new records? Is this worse than an if exists check?
The table is indexed on all relevant columns. I assume for posting 600,000 entries there would be a big advantage to checking beforehand, but the page in question will have 100 entries at most.
The biggest problem you're going to have with performance here is that you are calling a stored procedure for every entry - it really doesn't matter if inside that stored procedure you use DELETE/INSERT or check first, you're still going to have the overhead of 600K procedure calls, some potentially large portion of 600K logged transactions, etc.
I strongly recommend you look at table-valued parameters. Your C# or whatever can pass a set of 600K entries to a single stored procedure, once, and then you can perform two set-based operations (pseudo-code):
UPDATE src SET val = t.val
FROM dbo.tvp INNER JOIN dbo.source AS src
ON t.key = src.key;
INSERT src SELECT x FROM dbo.tvp AS t
WHERE NOT EXISTS (SELECT 1 FROM src WHERE key = t.key);