Use values from adjacent rows in different columns of a mysql table - mysql

Table mytable in mysql has columns col1 and col2 with integer not null values. It also has a primary key id. After arranging the columns in a certain order, I need to calculate a certain sum from all the elements of columns col1 and col2 as follows: For a given value in col2 in a given row, compare it with the value of col1 in the next row. Take the smaller of these two values and subtract from it the value of col1 in the row where the value of col2 was taken; for the last row just subtract from the value in col2 the value in col1 in that row. Get the sum of all these differences.
I have the following solution which seems to work but might disturb where the user lacks privileges to create tables or alter the table structure. It would require checking existence of a table and some columns before they are created, besides needing to clean up after running, to remove the columns added.
alter table mytable add my_id int(5) not null;
alter table mytable add col3 int(7) not null;
SET #count = 0;
UPDATE mytable SET my_id = #count:= #count + 1;
create temporary table mytable2 as select my_id, col1 from mytable limit 1, 99999;
update mytable a join mytable2 b on a.my_id = (b.my_id - 1) set a.col3 = b.col1;
update mytable set col3 = col2 order by my_id desc limit 1;
select sum(LEAST(col2,col3) - col1) from mytable;
The Question: What would be a better way of getting the desired sum described above?
Here is some sample data:
id col1 col2
--------------------
11 609 618
42 620 628
23 624 630
34 627 637
Required Sum: (618-609) + (624-620) + (627-624) + (637-627) = 26

Related

Select updated records in mysql

How can I select Ids of updated records in mysql;
Is there any way to do something like this
select Id from (update tblA set col1=false where col2 > 5 )
You can't, you have to select them before updating.
If your table uses InnoDB storage engine, use SELECT FOR UPDATE to lock selected rows and prevent other clients from inserting, updating or deleting selected rows.
START TRANSACTION;
SELECT id FROM tblA WHERE col2 > 5 FOR UPDATE;
UPDATE tblA SET col1 = false WHERE col2 > 5;
COMMIT;
Another alternative is to set some column to a value which is unique to your client (e.g. process id) and when select using that value.
But it requires an additional column.
UPDATE tblA SET col1 = false, process_id = 12345 WHERE col2 > 5 AND process_id IS NULL;
SELECT id FROM tblA WHERE process_id = 12345

SQL how to use a formula to fill a column like in Excel

My database is in MySQL
I have a table, let's say of 4 columns.
I would like to know if it's possible, and how to implement the following: fill the 4th column according to the value of the column 2 and column 3
In Excel I have a formula, let's give an example: if column2 value is set to "grey" and column3 value is set to "car", then column 4 value should be set to "super"
I just say this as an example.
My real formula in Excel looks like this: =IF(K4=4;"Maximal";IF(K4>4;"Maximal";IF(K4=3;"Important";IF(K4>3;"Important";IF(K4=2;"Limited";IF(K4>2;"Limited";IF(K4=1;"Forgettable";IF(K4>1;"Forgettable";"error"))))))))
However I want to do it in SQL.
I was thinking of creating my table until the column 3, set column 4 to NULL or empty, then open a GUI written in Java and maybe there do a piece of code to automatically fill the column 4 according to what is in column 2 and column 3 (these values will be choosable via Choicelist).
But if there is a way to do it directly in SQL, I am interested
Thx a lot in advance for your help.
regards
Yes. you can easily update your NULL-values according to some requirements for the other values in other columns of a particular row with the Update statement
UPDATE <tablename>
SET <column> = 'value'
WHERE <condition>
The only drawback here might be that you have to create an update statement for each of the combinations of your values in column2 and column3. (however, it's not much work for your amount of conditions).
I created an example (demo):
Creating a table in SQL according to your example could look like this,I used a temporary one for the sake of an example:
CREATE GLOBAL TEMPORARY TABLE demoTable (
"Col1" VARCHAR2(50 BYTE) NOT NULL,
"Col2" VARCHAR2(50 BYTE) NOT NULL,
"Col3" VARCHAR2(50 BYTE) NOT NULL,
"Col4" VARCHAR2(50 BYTE) DEFAULT NULL
)
ON COMMIT PRESERVE ROWS
I also inserted some dummy data:
INSERT INTO demoTable VALUES ('Charles', 'grey', 'car', NULL);
INSERT INTO demoTable VALUES ('Alice', 'grey', 'bike', NULL);
INSERT INTO demoTable VALUES ('Bob', 'red', 'car', NULL);
The result:
Now, create the update statements like this, for example:
UPDATE demoTable dt
SET dt."Col4" = 'super'
WHERE dt."Col2" = 'grey' AND dt."Col3" = 'car';
The result
You can try like this;
select * from mytable
COL1 COL2
---- --------------------
0 -
1 -
2 -
3 -
4 -
4 record(s) selected.
update mytable Set Col2 =
Case
When Col1<1 Then 'error'
When Col1=1 Then 'Forget'
When Col1=2 Then 'Limited'
When Col1=3 Then 'Important'
When Col1=4 Then 'Maximal'
End"
select * from mytable"
COL1 COL2
---- --------------------
0 Error
1 Forget
2 Limited
3 Important
4 Maximal
4 record(s) selected.
You can create a sql function, lets say udfGetColumn4Value taking in the column2, column3 as parameters to it and return a value.
Now you can run a select column2, column3, udfGetColumn4Value(column2, column3) from table or a query as desired. Hope this helps.
You were not very precise regarding which DBMS you're using. And also about the exact logic behind using your two columns.
Still here comes a probable SQL-Server solution, where I have taken one statement using CASE WHEN with your example and concatenated your two columns col2 and col3 (you can apply your further logic of here) otherwise:
UPDATE TableName
SET Col4 = CASE WHEN col2 = 'red' AND col3 = 'car' THEN 'super' ELSE col2 + col3 END;
You should replace col2 + col3 with your further logic.
Seems that a simple UPDATE-Query could address your problem:
update things set result = "super" where thing = "car" and color = "grey";
The where-clause does what you desire to do by saying
fill the column 4 according to what is in column 2 and column 3
I created a test table here on turorialspoint, there you can check if it fits your needs.

If a Column is incremented and then used in the same UPDATE query to set another Column's value, which value will it hold?

Given the query...
UPDATE
Table1
SET
col1 = col1 + 1,
col2 = col1 * 2
WHERE
colID = 1
...if col1 equals 1 before the query is executed, will col2 be set to 2 or 4 after the query is executed?
Just give it a try. But here you go:
http://sqlfiddle.com/#!2/7b6de/1
Col2 updates to 4.
Good luck.

Return columns which has NULL or 0 Values in a row

I've a Table with 10 fields. Each field or column contains Integer values.
Now I need only field(s) to be returned which has Null or 0 in the result set.
This worked for me. For instance:
Instead of:
where column_name is null or column_name = 0
It would be:
where COALESCE(column_name,0) = 0
Posted by Jon Gabrielson on December 18, 2002
The function 'COALESCE' can simplify working with null values. for
example, to treat null as zero, you can use: select
COALESCE(colname,0) from table where COALESCE(colname,0) > 1;
Use where column_name is null or column_name = 0
It's not clear what you are asking.
Can you elaborate a bit on what the resultset should look like, do you want all 10 columns returned, but only include the rows that have at least one column containing NULL or 0? That's very easy to do, by specifying appropriate predicates in the WHERE clause.
SELECT col0, col1, col2, col3, col4, col5, col6, col7, col8, col9
FROM mytable
WHERE IFNULL(col0,0) = 0
OR IFNULL(col1,0) = 0
OR IFNULL(col2,0) = 0
OR IFNULL(col3,0) = 0
OR IFNULL(col4,0) = 0
OR IFNULL(col5,0) = 0
OR IFNULL(col6,0) = 0
OR IFNULL(col7,0) = 0
OR IFNULL(col8,0) = 0
OR IFNULL(col9,0) = 0
That will return all rows that have a zero or NULL in at least one of the specified columns.
But your question seems to be asking about something a little bit different; you seem to be asking about returning only certain columns based on conditions. The columns to be returned in the result set are determined by the list of expressions following the SELECT keyword. You can't dynamically alter the expressions in the SELECT list based on the values the column contain.
To return the names of the columns which have at least one row that contains a NULL or zero in that column, you could write a query like this (this is limited to 5 columns, could be easily extended to 10 or more columns):
SELECT 'col0' AS col_name FROM mytable WHERE IFNULL(col0,0) = 0
UNION SELECT 'col1' FROM mytable WHERE IFNULL(col1,0) = 0
UNION SELECT 'col2' FROM mytable WHERE IFNULL(col2,0) = 0
UNION SELECT 'col3' FROM mytable WHERE IFNULL(col3,0) = 0
UNION SELECT 'col4' FROM mytable WHERE IFNULL(col4,0) = 0
(That query is going to do some serious scanning through the table. If indexes are available, the predicates can be rewritten to allow for index range scan.)
Here's a way to to the column_names in a single row. (A NULL in one of the columns would mean that the column does not contain any zeros or NULL.)
SELECT (SELECT 'col0' FROM mytable WHERE IFNULL(col0,0)=0 LIMIT 1) AS col0
, (SELECT 'col1' FROM mytable WHERE IFNULL(col1,0)=0 LIMIT 1) AS col1
, (SELECT 'col2' FROM mytable WHERE IFNULL(col2,0)=0 LIMIT 1) AS col2
, (SELECT 'col3' FROM mytable WHERE IFNULL(col3,0)=0 LIMIT 1) AS col3
, (SELECT 'col4' FROM mytable WHERE IFNULL(col4,0)=0 LIMIT 1) AS col4
But it would be much faster to do a single scan through the table:
SELECT IF(c0>0,'col0',NULL)
, IF(c1>0,'col1',NULL)
, IF(c2>0,'col2',NULL)
, IF(c3>0,'col3',NULL)
, IF(c4>0,'col4',NULL)
FROM ( SELECT SUM(IF(IFNULL(col0,0)=0,1,0)) AS c0
, SUM(IF(IFNULL(col1,0)=0,1,0)) AS c1
, SUM(IF(IFNULL(col2,0)=0,1,0)) AS c2
, SUM(IF(IFNULL(col3,0)=0,1,0)) AS c3
, SUM(IF(IFNULL(col3,0)=0,1,0)) AS c4
FROM mytable
)
Do you need the columns that have NULL or 0, or the rows with a column that is NULL or 0, or the rows where all columns are either NULL or 0?
But I doubt you would have 10 equivalent columns in a well-designed table, so you might want to re-design your data model -- make the columns rows in another table, and join the two tables together.
You might have something like this:
CREATE TABLE a(a_id int primary key, b0 int, b1 int, b2 int, ..., b9 int );
But you want this:
CREATE TABLE a( a_id int primary key );
CREATE TABLE b( b_id int primary key );
INSERT INTO b (b_id) values (0), (1), ..., (9);
CREATE TABLE ab (
a_id int, b_id int,
v int not null, -- this would be the value of b0, b1, ...
foreign key (a_id) references a(a_id),
foreign key (b_id) references b(b_id),
primary key(a_id, b_id)
);
And then you can write something like:
SELECT * FROM a, b -- cross join to get all possible combinations
WHERE (a_id, b_id) NOT IN (
-- then remove the ones that have a value
SELECT a_id, b_id FROM a JOIN ab ON a.id = ab.a_id JOIN b ON ab.a_id
WHERE ab.v <> 0);
The last line is unnecessary if, before running it, you delete all 0'd lines:
DELETE FROM ab WHERE v = 0;
I recently had to make a similar method in another language. The logic was to check that all "columns" in a record were "true" before it could be processed.
The way I got around it was with this logic:
# loop through the records
for ($i=0; $i<count($records); $i++) {
# in each record, if any column is 0 or null we will disgard it
# this boolean will tell us if we can keep the record or not
# default value is 'true', this gives it a chance to survive
$keepit = true;
# loop through each column
for ($j=0; $j<count($records[$i]); $j++) {
# add your boolean condition (true=go ahead, false=cant go ahead)
$goahead = (!is_null($records[$i][$j]) && $records[$i][$j] != 0);
# convert the boolean to a 0 or 1,
# find the minimum number in a record
# (so if any field in a record is false i.e. 0 or null, dont use the record)
$keepit = min(+$goahead, +$keepit);
}#end of col loop
if ($keepit) {
# keep the record
array_push($anotherArray, $records[$i]);
}
}
I've written this in PHP for you. It's probably doable in MySQL too but I'd recommend you fetch all of the records, process them in PHP, and send them back to MySQL.
In future, you should design your database table so that invalid records are not allowed/not stored in the first place.

mysql - union with creating demarcated field

I need UNION two tables with creating new field, where 1 for first table, and 2 for second.
I tried
(
SELECT field, 1 AS tmp
FROM table1
)
UNION
(
SELECT field, 2 AS tmp
FROM table2
)
But in result, tmp field was full of "1".
How it can be implemented?
Your query should work fine. The only thing you should change is UNION should be UNION ALL to give better performance. Without the ALL it defaults to UNION DISTINCT which causes the rows to be compared for duplicates*, but the way you have constructed them guarantees that there cannot be duplicates so this extra check is a waste of time. Here is some test code I used to verify that what you are doing ought to work:
CREATE TABLE table1 (field NVARCHAR(100) NOT NULL);
INSERT INTO table1 (field) VALUES
('foo1'),
('bar1'),
('baz1');
CREATE TABLE table2 (field NVARCHAR(100) NOT NULL);
INSERT INTO table2 (field) VALUES
('foo2'),
('bar2'),
('baz2');
SELECT field, 1 AS tmp
FROM table1
UNION ALL
SELECT field, 2 AS tmp
FROM table2
Result:
'foo1', 1
'bar1', 1
'baz1', 1
'foo2', 2
'bar2', 2
'baz2', 2
If you only get rows where tmp was equal to 1, maybe your table2 was empty?
*See the documentation for UNION.
The default behavior for UNION is that duplicate rows are removed from the result. The optional DISTINCT keyword has no effect other than the default because it also specifies duplicate-row removal. With the optional ALL keyword, duplicate-row removal does not occur and the result includes all matching rows from all the SELECT statements.
You are very close
Create YourNewTable
SELECT field, 1 AS tmp
FROM table1
UNION ALL
SELECT field, 2 AS tmp
FROM table2