Create column corresponding to entry rank position in SQL - mysql

I am using SQL and I have a table that looks like:
variable1 123
variable2 39
variable3 993
variable4 2
and I would like to add a column that corresponds to the rank of the integer, had that column been sorted. ie:
variable1 123 3
variable2 39 2
variable3 993 4
variable4 2 1
So far I haven't been able to get the syntax correct - I can sort or order by that column but not generate the correct ranking variable.
Many thanks.

The best performant way is to use variables:
select t.*,
(#rn := #rn + 1) as rank
from table t cross join
(select #rn := 0) params
order by intcol;
Note: this technically does a row_number() but the two are equivalent for your sample data, because there are no ties.

it sounds like you'd need to create a trigger before insert that would update that column to increase the rank integer for each variable that held a value higher than the value which you are about to insert by 1.

Related

Creating new data table on existing one

Hello I've got a question, how (if it possible), I can create new datatables with close same rows but if In column value is in string "/" for example
ID
column_param
column_sym
column_value
column_val2
First
param_test1
ABC
11/12
test
Second
param_test2
CDE
22/11
test
Third
param_test3
EFG
44
teste
4'th
param_test4
HIJ
33/22
test
And here if I have param_test1 and param_test4 and if in this column value has "/" I want to create 2 other rows but if I will not set param_test2 then it stay as it is and everything should be in new datatable. Is any way to create this?
Thank you in advance.
Expected result:
As per Gordon's answer, I'm not sure what should be done with the your ID column.
I've replaced these with row numbers.
Depending on your version of MySQL/MariaDB, the ROW_NUMBER() window function may not be available. Depending on whether IDs in the output are necessary you may be able to simply omit this.
I've assumed the existence of a table called myNumbers which contains a single field num and is populated with positive integers from 1 to whatever you're likely to need.
I've included more in the output that you asked for, which will hopefully help you understand what's going on
SELECT
ROW_NUMBER() OVER (ORDER BY d.ID, n.num) as NewID,
d.ID as OriginalID,
n.num as,
d.column_param,
d.column_sym,
d.column_value as orig_value,
CASE WHEN column_param = 'param_test2' THEN d.column_value
ELSE substring_index(substring_index(d.column_value,'/',n.num),'/',-1) END as split_value,
d.column_val2
FROM
myData d
JOIN myNumbers n on char_length(d.column_value)-char_length(replace(d.column_value,'/','')) >= n.num-1
WHERE
n.num = 1 OR d.column_param <> 'param_test2'
ORDER BY
d.ID,
n.num
See this DB Fiddle (the columns output in a different order than I've specified, but I think that's a DB Fiddle quirk).
If you only want to "split" say param_test1 and param_test4 rows the code above code could be amended as follows:
SELECT
ROW_NUMBER() OVER (ORDER BY d.ID, n.num) as NewID,
d.ID as OriginalID,
d.column_param,
d.column_sym,
n.num,
d.column_value as orig_value,
CASE WHEN column_param NOT IN ('param_test1','param_test4') THEN d.column_value
ELSE substring_index(substring_index(d.column_value,'/',n.num),'/',-1) END as split_value,
d.column_val2
FROM
myData d
JOIN myNumbers n on char_length(d.column_value)-char_length(replace(d.column_value,'/','')) >= n.num-1
WHERE
n.num = 1 OR d.column_param IN ('param_test1','param_test4')
ORDER BY
d.ID,
n.num
I don't know how the id is being set, but you can do what you want using union all:
select column_param, column_sym,
substring_index(column_value, '/', 1) as column_value,
column_val2
from t
union all
select column_param, column_sym,
substring_index(column_value, '/', -1) as column_value,
column_val2
from t
where column_value = '%/%';

Keep the newest one field value until it changes then keeping its newest field value

I have a few tables that have millions of records where a sensor was sending multiple 0 and 1 values and this data was logged to the table even though we only needed it to keep the very first 1 or 0 per each 1 to 0 or 0 to 1 change.
Adjustments have been made so we only now get the 1 and 0 values on each change and not every one second or whatever but I need to cleanup the unnecessary records from the tables.
I've done some research and testing and I'm having trouble figuring out what method to use here to delete the records not needed. I was trying to figure out how to retain the previous value record using variables and also created row numbers but it's not working as I need it to.
I created an SQLFiddle here and tried some logic per the example post MySQL - How To Select Rows Depending on Value in Previous Row (Remove Duplicates in Each Sequence). I keep getting back no results from this and when I tried running it on a large local MySQL table, and I got an error wto I have to increase the MySQL Workbench read query timeout to 600 or it lost connection.
I also found the "MySql - How get value in previous row and value in next row?" post and tried some variations of it and also "How to get next/previous record in MySQL?" and I've come up with total failure getting the expected results.
The Data
The data in the tables has a TimeStr column and a Value column just as in the screen shot and on the SQLFiddle link I posted with a small sample of the data.
Each record will never have the same TimeStr value but I really only need to keep the very first record time wise when the sensor either turned ON or OFF if that clarifies.
I'm not sure if the records will need an incremental row number added to get the expected results since it only has the TimeStr and the Value records otherwise.
My Question
Can anyone help me determine a method that I can use on a couple large tables to delete the records from a table where there are subsequent and duplicate Value values so the tables only has the very first 1 or 0 records where those actually change from a 1 to 0 or 0 to 1?
I will accept an answer that also results in just the records needed—but any that perform fast would be even more greatly appreciated.
I can easily put those into a temp table, drop the original table, and then create and insert the needed records only into the original table.
Expected Results
| TimeStr | Value |
|----------------------|-------|
| 2018-02-13T00:00:00Z | 0 |
| 2018-02-13T00:00:17Z | 1 |
| 2018-02-13T00:00:24Z | 0 |
| 2018-02-13T00:00:28Z | 1 |
Select t.timestr, t.value from (
SELECT s.*, #pv x1, (#pv := s.value) x2
FROM sensor S, (select #pv := -1) x
ORDER BY TimeStr ) t
where t.x1 != t.x2
See http://sqlfiddle.com/#!9/8d0774/122
Try this :
SET #rownum = 0;
SET #rownum_x = 0;
SELECT b.rownum, b.TimeStr, b.Value
FROM
(
SELECT #rownum := #rownum+1 as rownum, TimeStr, Value
FROM sensor
ORDER BY TimeStr
) b
LEFT JOIN (
SELECT #rownum_x := #rownum_x+1 as rownum_x, TimeStr as TimeStr_x, Value as Value_x
FROM sensor
ORDER BY TimeStr
) x ON b.rownum = x.rownum_x + 1
where b.Value <> x.Value_x or x.Value_x is null
order by b.TimeStr
The result I got is
You want the first record for each value when it appears. This suggests variables. Here is one way that only involves sorting and no joining:
select t.*
from (select t.*,
(case when value = #prev_value then value
when (#save_prev := #prev_value) = NULL then NULL
when (#prev_value := value) = NULL then NULL
else #save_prev
end) as prev_value
from (select t.*
from sensor t
order by timestr
) t cross join
(select #prev_value := -1) params
) t
where prev_value <> value;
Notes:
The subquery for ordering only seems to be needed since MySQL 5.7.
The case is just a way to introduce serialized code. When using a variable it should only be used on one expression.
This only requires one sort -- and if you have an index, that doesn't even need to be a sort.
Here is a SQL Fiddle.

sql new column by delimiter with order ID

Hi this maybe a simple one but I need help specifically for MYsql
I have the data in one column lets call the column WORK 1,2,3,5,2 (these values are sometimes longer and shorter or more values are present e.g 12,15,11,15,16,143)
I need these to be put into 1 new column for each delimiter and have an ID for the order presented. e.g output
SELECT
*
FROM (SELECT
ROW_NUMBER()
OVER (ORDER BY WORK) AS Row,
RIGHT(LEFT(T.WORK, Number - 1),
CHARINDEX(',', REVERSE(LEFT(',' + T.WORK, Number - 1)))) AS a
FROM master..spt_values,
<YOUR_TABLENAME> T
WHERE Type = 'P'
AND Number BETWEEN 1 AND LEN(T.WORK) + 1
AND (SUBSTRING(T.WORK, Number, 1) = ','
OR SUBSTRING(T.WORK, Number, 1) = '')) AS A

SQL sort column A & update column B

I have a table with data similar to:
Inven DESCRIPT PrintOrder
---------------------------------
1 D 9
2 B 0
3 A 5
4 Z 0
5 X 1
. . .
. . .
. . .
I would like to sort the table on column DESCRIPT descending alpha (A - Z) and then update the column PRINTORDER so that when done, the record with PRINTORDER = 1 is the the highest alpha (A) and the record with the highest value for PRINTORDER will be the lowest in alpha (Z).
Is this possible without using temporary columns? Not a deal breaker if not, just a preference.
DESIRED RESULT:
To update the PrintOrder values based on the sorting result
Inven DESCRIPT PrintOrder
---------------------------------
1 D 3
2 B 2
3 A 1
4 Z 5
5 X 4
I am unclear whether you want to modify the table or just create a result set. Here is the solution to the latter using standard SQL:
select Inven, Descript, row_number() over (order by Descript) as PrintOrder
from table t
order by Descript;
EDIT:
In MySQL, the select would look like:
select t.*
from (select Inven, Descript, (#rn := #rn + 1) as PrintOrder
from table t cross join (select #rn := 0) params
order by Descript
) t
order by Inven;
The update is slightly trickier, because you cannot sort and initialize a variable:
update t
set PrintOrder = (#rn := coalesce(#rn + 1, 1))
order by Descript;
This should do it:
Update T
set PRINTORDER = ASCII(UPPER(DESCRIPT)) - 64
while the ASCII of A is 65 in your system. :)
EDIT:
As you said there might be something more, so we need to change it a bit by assuming you have a PK column name as Id:
UPDATE T
set PRINTORDER = T2.Order
FROM T
INNER JOIN (SELECT IDENTITY(int, 1,1) AS Order,Id FROM T Order By DESCRIPT ASC) T2 ON T.Id=T2.Id
But in this way even two record has same DESCRIPT would end up have different printorder, it could be a bit more complex if you want keep them the same.
EDIT:
Sorry just realized you are using MySql, the IDENTITY(int, 1,1) is from SQL Server, basically it generate a row number, you might need something different to have one: http://blog.sqlauthority.com/2014/03/08/mysql-generating-row-number-for-each-row-using-variable/
After arriving at my office, and chatting with my colleague, the question posed is completely inaccurate. So I'll close this thread, and an open a new one with a more accurate description of the issue.
Thank you Gordon & Simon for your responses, they are most appreciated.

How count the number of rows starting from a specified row?

I have a query problem about retrieving number of rows that the counting will start from the specified row. These are the row values on my MySQL database.
John Parker
Tony Graham
Perter Smith
Annabelle Sergio
Kris Pata
Neshren Luca
Paul Pon
Zervich Nuckrav
Allan Paulson
Imanu Hashmarida
Varick Dagovich
Senster Burgsman
Lito Umani
Ramsay Nudillo
Now I want to retrieve the first 5 row that will start from Neshren Luca so that the resultset may look like this:
Neshren Luca
Paul Pon
Zervich Nuckrav
Allan Paulson
Imanu Hashmarida
How may I do that?
Note: I will not try to retrieve the row values base on number of row but base from a specific row value.
This looks very ugly but have this a try, this uses local variable
SET #selectedName := 'Neshren Luca'; -- set the name here
SET #selectRow :=
(
SELECT RankNo
FROM
(
SELECT #rowNum := #rowNum + 1 AS RankNo,
a.Names
FROM tableName a, (SELECT #rowNum:= 0) b
) x
WHERE Names = #selectedName
);
SELECT Names
FROM
(
SELECT #rowNum1 := #rowNum1 + 1 AS RankNo, a.Names
FROM tableName a, (SELECT #rowNum1:= 0) b
) x
WHERE RankNo BETWEEN #selectRow AND #selectRow + 4
SQLFiddle Demo
Looks like the above select statement will work fine for that..other wise you have to do other way but it will bit lengthy..that..
-> You have to get the above records into a cursor
-> Looping through the cursor and getting the records..
SQL engine may have some 'natural' row order, but it is not guaranteed. You should have primary key, say ID, then ORDER BY ID, find first ID WHERE your string is. And then select first N items WHERE ID >= that id.
Edit: Or, if your database guarantees natural order, find row id/index and LIMIT/OFFSET by this value. SQLite has built-in natural rowid for each table row, for example.