update sql table current row - mysql

Complete noob alert! I need to store a largish set of data fields (480) for each of many devices i am measuring. Each field is a Decimal(8,5). First, is this an unreasonably large table? I have no experience really, so if it is unmanageable, I might start thinking of an alternative storage method.
Right now, I am creating a new row using INSERT, then trying to put the 480 data values in to the new row using UPDATE (in a loop). Currently each UPDATE is overwriting the entire column. How do I specify only to modify the last row? For example, with a table ("magnitude") having columns "id", "field1", "field2",...:
sql UPDATE magnitude SET field1 = 3.14; this modifies the entire "field1" column.
Was trying to do something like:
sql UPDATE magnitude SET field1 = 3.14 WHERE id = MAX(id)
Obviously I am a complete noob. Just trying to get this one thing working and move on... Did look around a lot but can't find a solution. Any help appreciated.

Instead of inserting a row and then updating it with values, you should insert an entire row, with populated values, at once, using the insert command.
I.e.
insert into tTable (column1, column2, ..., column n) values (datum1, datum2, ..., datum n)
Your table's definition should have the ID column with property identity, which means that it will autofill it for you when you insert, i.e. you don't need to specify it.
Re: appropriateness of the schema, I think 480 is a large number of columns. However, this is a straightforward enough example that you could try it and determine empirically if your system is able to give you the performance you need.
If I were doing this myself, I would go for a different solution that has many rows instead of many columns:
Create a table tDevice (ID int, Name nvarchar)
Create a table tData (ID int, Device_ID int, Value decimal(8,5))
-- With a foreign key on Device_ID back to tDevice.ID
Then, to populate:
Insert all your devices in tDevice
Insert one row into tData for every Device / Data combination
-- i.e. 480 x n rows, n being the number of devices
Then, you can query the data you want like so:
select * from tData join tDevice on tDevice.ID = tData.Device_ID

Related

extract data from sql, modify it and save the result to a table

This may seem like a dumb question. I am wanting to set up an SQL db with records containing numbers. I would like to run an enquiry to select a group of records, then take the values in that group, do some basic arithmetic on the numbers and then save the results to a different table but still have them linked with a foreign key to the original record. Is that possible to do in SQL without taking the data to another application and then importing it back? If so, what is the basic function/procedure to complete this action?
I'm coming from an excel/macro/basic python background and want to investigate if it's worth the switch to SQL.
PS. I'm wanting to stay open source.
A tiny example using postgresql (9.6)
-- Create tables
CREATE TABLE initialValues(
id serial PRIMARY KEY,
value int
);
CREATE TABLE addOne(
id serial,
id_init_val int REFERENCES initialValues(id),
value int
);
-- Init values
INSERT INTO initialValues(value)
SELECT a.n
FROM generate_series(1, 100) as a(n);
-- Insert values in the second table by selecting the ones from the
-- First one .
WITH init_val as (SELECT i.id,i.value FROM initialValues i)
INSERT INTO addOne(id_init_val,value)
(SELECT id,value+1 FROM init_val);
In MySQL you can use CREATE TABLE ... SELECT (https://dev.mysql.com/doc/refman/8.0/en/create-table-select.html)

increase the value of only one element in a set

I have a column with sets of numbers like 21,8,0,345,... if I wanted to +1 to only one element in the set, for example element 3, would I be able to do that with a mysql query?
I know I can do this with php code (explode the string to an array then update it) but I'm afraid that with multiple updates simultaneously on the same row the values will be rewritten.
First query will set 21,8,1,345 and the second will rewrite it with 21,9,0,345
Replacing the element in question might also not work because some rows have multiple elements with the same value like 2,40,40,41
MySQL supports "SELECT ... FOR UPDATE" specifically for this situation to make sure the row isn't overwritten while you're processing the the row contents.
https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
The above link even gives a very similar example (except for exploding the elements, increasing the one you want, and imploding them back together).
SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;
The better answer, as Tim suggested, is to store this data in a separate table, especially since you have a variable number of items for each row. I don't know how you currently know that you want to update, say, the 3rd item but I'll assume that's known.
Let's say these numbers are temperature readings from various sensors at a "location" and they gradually go up and down. Your main table is "locations" with with fields:
id (int, auto-increment), location_name (varchar), ...
You're then going to create a new table called "readings" with fields:
id (int, auto-increment), location_id (int), temperature (smallint)
The "id" from the first table is going to match up to the "location_id" of many records in "readings".
When you want to add a new temperature reading to a location (I'm assuming you'll have a $location_id and $new_reading variables in PHP):
INSERT INTO readings (location_id, temperature)
VALUES ( $location_id, $new_reading )
(NOTE: You should be properly sanitizing your inputs, using PDO, or other library, but that's out of scope for this answer or I'm going to be here all night. :-) )
Let's say you want to update the 3rd reading for this location, that would mean the "offset" is 2 and you only want to update 1 record so that's what "LIMIT 2, 1" means below. (I tried and failed to find a way to do this in only 1 query; UPDATE does not seem to support offsets, at least not in my version of MySQL.)
SELECT id FROM readings WHERE location_id = 1 ORDER BY id LIMIT 2, 1;
/* Let's say you stored the above result in $reading_id */
UPDATE readings SET temperature = temperature + 1 WHERE id = $reading_id;

SQL Table - How to add a row at the start of an old autoincrement column

I have an existing sql table with 3 columns and 100+ entries/rows. There is an id column with autoincrement.
Now, I want to add 10 new rows at the beginning of the table with id from 1 to 10. But I cannot lose any existing row. So, how do I do it?
One idea that just came to my mind is perhaps I can increase the existing id by adding 10, like 1+10 becomes 11, 25+10 becomes 35, and then I can add rows at the beginning. What will be the script for this IF this is possible?
All you need to do for this is to set the auto_increment for that table to whatever number you need to create space for the new records you want to insert.
For example, if you inserted rows with id's 1-100, you might:
Check the next auto_increment value by running:
select auto_increment as val from information_schema.tables where table_schema='myschema' and table_name='mytable';
Let's assume that value would be 101 (the value that would be used if you inserted a new row). You can "advance" the auto_increment value by running:
alter table myschema.mytable auto_increment = 111;
If you insert a new row like this:
insert into mytable (not_the_id_column) values ('test');
It will get the "next" id of 111. But if you specify id values manually, you are ok in this case as long as you use any value less than 111, so you could insert your desired records like this:
insert into mytable (id, not_the_id_column) values (101, 'test101');
insert into mytable (id, not_the_id_column) values (102, 'test102');
... -- more inserts as needed
Now, you still must take proper precautions when updating PK values, or any value that has dependencies on it (Foreign Key or otherwise), but it is completely legitimate to forcibly advance and/or backfill the id values, as long as the resulting auto_increment value doesn't duplicate one that's already in the table.
I agree with juergen d's comment that you should not do this, but I realize there are situations where this kind of thing must be done.
SELECT MAX(id)-MIN(id)+1 INTO #x FROM theTable;
UPDATE theTable SET id = id + #x;
SELECT MIN(id) INTO #x FROM theTable;
UPDATE theTable SET id = 10 + id - #x;
If the id is the primary key, value collisions within an update can cause MySQL to reject the update. (Hence the pair of updates to avoid such a possibility.)
Edit: Factoring N.B.'s strong objection into this, it would also probably be good to verify the table's next auto-increment value is not going to collide with the updated records after the update is completed. I don't have an appropriate database on hand to verify whether UPDATE statements affect it; and even if they do affect it, you may end up wanting to reduce it so as to not create an unnecessary gap (gaps should ideally not be a problem, but if they are or you are just mildly OCD, it is worth looking into).

Creating an new field from mathematically manipulating values in other fields

Bit of a newbie here. I'm currently working on a MySQL table that lists the details for different cars. I need a new field that is built up of the information from three other fields. So I have 'Acceleration', 'Speed' and 'Braking' which all contain double digit integers that are averaged out to another field I want to call 'Average'.
The logic being 'Acceleration'+'Speed'+'Braking'/3
I can't seem to figure out the correct syntax to do this. I do specifically need this to be a field as I need those values to show up on other queries. I know a SELECT query can get the result values I need, but how to I conduct those values to a permanent field on that table?
Thanks in advance for any help on this.
First, you'd need to alter the table schema to define the new column:
ALTER TABLE my_table ADD COLUMN Average FLOAT;
Next, update the table to set the values:
UPDATE my_table SET Average = (Acceleration + Speed + Braking) / 3;
Consider how to correctly set Average for newly inserted/updated data. Perhaps use triggers:
CREATE TRIGGER calc_average_ins AFTER INSERT ON my_table FOR EACH ROW
SET NEW.Average = (NEW.Acceleration + NEW.Speed + NEW.Braking) / 3;
CREATE TRIGGER calc_average_upd AFTER UPDATE ON my_table FOR EACH ROW
SET NEW.Average = (NEW.Acceleration + NEW.Speed + NEW.Braking) / 3;
You might want to consider instead introducing this column in a view, to create the averages as required, on-the-fly, and thereby preventing it from becoming desynchronised from the underlying data values (but note you no longer achieve the performance benefit of having the values cached):
CREATE VIEW my_view AS
SELECT *, (Acceleration + Speed + Braking) / 3 AS Average FROM my_table;
Finally, note that your average has no physical meaning in the real world (what would be its units?): a more meaningful metric may or may not be more suitable to your needs.

Index counter shared by multiple tables in mysql

I have two tables, each one has a primary ID column as key. I want the two tables to share one increasing key counter.
For example, when the two tables are empty, and counter = 1. When record A is about to be inserted to table 1, its ID will be 1 and the counter will be increased to 2. When record B is about to be inserted to table 2, its ID will be 2 and the counter will be increased to 3. When record C is about to be inserted to table 1 again, its ID will be 3 and so on.
I am using PHP as the outside language. Now I have two options:
Keep the counter in the database as a single-row-single-column table. But every time I add things to table A or B, I need to update this counter table.
I can keep the counter as a global variable in PHP. But then I need to initialize the counter from the maximum key of the two tables at the start of apache, which I have no idea how to do.
Any suggestion for this?
The background is, I want to display a mix of records from the two tables in either ASC or DESC order of the creation time of the records. Furthermore, the records will be displayed in page-style, say, 50 records per page. Records are only added to the database rather than being removed. Following my above implementation, I can just perform a "select ... where key between 1 and 50" from two tables and merge the select datasets together, sort the 50 records according to IDs and display them.
Is there any other idea of implementing this requirement?
Thank you very much
Well, you will gain next to nothing with this setup; if you just keep the datetime of the insert you can easily do
SELECT * FROM
(
SELECT columnA, columnB, inserttime
FROM table1
UNION ALL
SELECT columnA, columnB, inserttime
FROM table2
)
ORDER BY inserttime
LIMIT 1, 50
And it will perform decently.
Alternatively (if chasing last drop of preformance), if you are merging the results it can be an indicator to merge the tables (why have two tables anyway if you are merging the results).
Or do it as SQL subclass (then you can have one table maintain IDs and other common attributes, and the other two reference the common ID sequence as foreign key).
if you need creatin time wont it be easier to add a timestamp field to your db and sort them according to that field?
i believe using ids as a refrence of creation is bad practice.
If you really must do this, there is a way. Create a one-row, one-column table to hold the last-used row number, and set it to zero. On each of your two data tables, create an AFTER INSERT trigger to read that table, increment it, and set the newly-inserted row number to that value. I can't remember the exact syntax because I haven't created a trigger for years; see here http://dev.mysql.com/doc/refman/5.0/en/triggers.html