Horizontally 'collapse' two columns with matching substrings into one - sql-server-2008

My query results in a table that looks like this:
Unit25 Unit26 Seconds25 Seconds26
A2501 null 383042 null
A2502 A2602 354554 35147
A2503 A2603 344021 33388
A2504 null 359453 null
A2505 null 16702 null
A2507 null 14784 null
null A2608 null 16997
A2509 A2609 13092 358893
null A2610 null 291256
A2511 A2611 18770 369082
A2512 A2612 8647 null
A2513 null 65214 null
A25s are matched to corresponding A26s by a substring of their last two digits.
However, I would like to combine those A25 and A26 columns into something like this:
Unit TotalSeconds
01 383042
02 389701
03 377409
04 359453
05 16702
07 14784
08 16997
09 371985
10 291256
11 387852
12 8647
13 65214
I can get TotalSeconds by using ISNULL to turn those nulls into 0s and adding Seconds25 and Seconds26 together, but I'm not so sure how to turn Unit25 and Unit26 into one column of substrings. Any advice?

Coalesce (or isnull) the two columns together so you always get a value, it doesn't matter which one. Get the length of the data, and then substring the last two characters, getting two characters starting at length -1. Eg - "A2501" is length five, so you want to substring from position 4 to get 01.
SUBSTRING(Coalesce(Unit25, Unit26), Len(Coalesce(Unit25, Unit26)) - 1, 2)

You can try this.
select substring(Coalesce(unit25,unit26),4,2) as units,isnull(seconds25,0)+isnull(seconds26,0) from #t

Related

How can I separate a single column into 3 separate columns

Want to execute a query to view single date-month-year time column to separate date column, month column and year column.
eg
joining_date
01-JAN-22 12.00.00AM
to
joining_date|joining_month|joining_year
01 | JAN | 22
You have some ways of doing this:
If your data is always in this 01-JAN-22 12.00.00AM format , no matter what comes after 22, you can use substring.
select substring('01-JAN-22 12.00.00AM',1,2) as joining_date,
substring('01-JAN-22 12.00.00AM',4,3) as joining_month,
substring('01-JAN-22 12.00.00AM',8,2) as joining_year;
Result:
joining_date joining_month joining_year
01 JAN 22
Another option is converting the string to proper date datatype an use MySQL functions, like :
select DAY(str_to_date('01-JAN-22 12.00.00AM', "%d-%b-%y")) as joining_date,
MONTH(str_to_date('01-JAN-22 12.00.00AM', "%d-%b-%y")) as joining_month,
YEAR(str_to_date('01-JAN-22 12.00.00AM', "%d-%b-%y")) as joining_year ;
Result:
joining_date joining_month joining_year
1 1 2022
Fiddle
Use YEAR, MONTH and DAY syntax:
SELECT
YEAR(`joining_date`) as joiningYear,
MONTH(`joining_date`) as joiningMonth,
DAY(`joining_date`) as joiningDay
FROM tableName
If you want your month name, then use MONTHNAME:
SELECT
YEAR(`joining_date`) as joiningYear,
MONTHNAME(`joining_date`) as joiningMonth,
DAY(`joining_date`) as joiningDay
FROM tableName

MySQL - How To Query Results With Match Of Middle Text/Digits

Imagine a column in table with digits and text, from 1001 to fa00ty and even 01100001 or longer, there's millions of rows of data. I want to query the table and return rows with only "00" or maybe just "0" in the "exact middle of the text/digits of the column". Example table
|some00e |
|01100001 |
|fa00ty |
|m00ol |
|23_00_asd|
|some long string with 00 some long string with|
There are only two matching results for "00" which would be fa00ty as 00 is exactly center of this text and 'some long string with 00 some long string with' and two for the single 0 query. So odd or even length does not matter.
Personally, I'd go with something like this:
SELECT stuff
FROM test
WHERE
(substring(stuff FROM (0-length(stuff)/2)-1 for 2) = "00"
AND (length(stuff) % 2 = 0))
OR
(substring(stuff FROM (0-ROUND(length(stuff)/2)) for 1) = "0"
AND (length(stuff) % 2 != 0))
This will only return even numbers (i.e. so the exact middle can be two digits) whose middle digits are '00', or odd numbers whose middle digit is '0'.
Following query works much faster. Still unsure if it is the best answer as I am not query knowledgeable, thus awaiting other possible solutions. Also will not work with anything greater than 3 digits as far as I could test.
For 2 digits
MID(`stuff`,round(LENGTH(`stuff`)/2),2) = '00' AND LENGTH(`stuff`)%2=0
For 1 digit
MID(`stuff`,round(LENGTH(`stuff`)/2),1) = '0' AND LENGTH(`stuff`)%2

How to Filter Out All column = null Rows Past the Last column != null Row, by an Offset, in the Result of a SQL Query

I have an SQL table that looks like this:
Flow Head Series_Name
0 null A
0 null B
10 null A
10 null B
20 null A
22.5 88 B
20 null B
30 null A
30 null B
39.42 60.1 A
40 null A
40 null B
etc... etc... etc...
5000 null A
5000 null B
Basically, the idea of this table is to be able to use it to plot Flow vs Head in a Line Chart element in Cognos Report Studio. This works nicely, since I can drag and drop the Flow column into the X-Axis, the Head column into the Y-Axis, and the Series_Name column into the Legend. For the example table above, this produces two curves, A and B.
Notice that Flow values are ranging from 0 to 5000, incrementing by 10, for both series. Notice also that Head values are mostly null, except when a data point is available. (Typically, data points' Flow values don't fall exactly on numbers that are divisible by 10, but if they do, it shouldn't matter for the problem I have.)
So, here's the problem I have. When I generate the Line Chart, everything looks great, except for those assets whose data points have lower Flow values. For these, the fixed X-Axis range of 0 to 5000 doesn't produce a nice looking Line Chart, since the curve is scrunched up against the Y-Axis. (The Y-Axis, by the way, looks just fine, since the Line Chart element automatically scales its Y-Axis.)
I know what needs to happen to fix this problem. I need to be able to write an SQL query to this table that filters out all rows with Head = null past whatever the last Head != null row is. It would also be nice to have an offset so that it didn't start to cut off until a few rows past the last Head != null row. This would make the chart look even nicer.
For the example table I gave above, if you assume that the Flow = 39.42 & Head = 60.1 row is the last Head != null row in the table, then a nice query result from this table would be the following, which excludes all the rows (by an offset of 6 rows) past it:
Flow Head Series_Name
0 null A
0 null B
10 null A
10 null B
20 null A
22.5 88 B
20 null B
30 null A
30 null B
39.42 60.1 A
40 null A
40 null B
50 null A
50 null B
60 null A
60 null B
I know this requires a WHERE clause at the end of the SELECT statement, but I'm not sure how to phrase such a clause in order to exclude rows only if they're part of the unnecessary latter part of the query result. The SQL query needs to be written in Native SQL syntax.
This sort of query result, which cuts off all unnecessary data, would graph beautifully! I appreciate the help!
I make this example in SQL SERVER same code should work in oracle
SELECT *
from Events
WHERE Flow <= (SELECT max(Flow) + 3*10 FROM Events WHERE [HEAD] IS NOT NULL)
3*10 to offset three more rows.
If you want something more generic and scalable you can use row_number() but need to be carefull with the order by
WITH addRow_id as (
SELECT *, ROW_NUMBER() OVER ( ORDER BY [Flow], [Series_Name]) as rn
FROM Events
),
lastID as (
SELECT MAX(rn) as last_row
FROM addRow_id
WHERE [HEAD] IS NOT NULL
)
SELECT *
FROM addRow_id A
CROSS JOIN lastID L
WHERE A.rn <= L.last_row + 6;
Possibly get everything below the cut off, and union that to everything above the cutoff but ordered by the flow with a limit of 10 applied:-
SELECT *
FROM sometable
WHERE flow <=
(
SELECT MAX(flow)
FROM sometable
WHERE head IS NOT NULL
)
UNION
SELECT flow, head, series_name
FROM
(
SELECT flow, head, series_name
FROM sometable
WHERE flow >
(
SELECT MAX(flow)
FROM sometable
WHERE head IS NOT NULL
)
ORDER BY flow
LIMIT 10
) sub1
But depends on the details of the order

Combine columns in result from SQL SELECT

I am trying to solve following problem:
I have a table with timeStamp column and three columns for different types of variables. The columns with values are filled randomly according to the conditions. So it looks like this:
smpl_time float_val num_val str_val 15:31
10 NULL NULL
15:32 NULL 15.4 NULL
15:33
NULL NULL Disabled
What I would like to achieve is a result that would merge all val fields into one column and sorts them according to timeStamp:
smpl_time merge_val
15:31 10
15:32 15.4
15:33 Disabled
So far I have a SELECT just for one val type looking like this (it contains also other field from the table that are common for each entry):
SELECT s.smpl_time AS Time,s.float_val AS Value, e.name AS Severity, m.name AS Status
FROM sample s JOIN channel c ON c.channel_id=s.channel_id JOIN severity e ON
e.severity_id=s.severity_id JOIN status m ON m.status_id=s.status_id WHERE
s.channel_id='id' AND smpl_time BETWEEN TIMESTAMP 'startTime' AND TIMESTAMP 'stopTime';
Does anyone have an idea how to do this or if it is even possible?
Cheers,
Mike
Assuming only one of the three columns is populated for any given row, use the COALESCE function.
SELECT smpl_time, COALESCE(float_vaL, num_val, str_val) AS merge_val
FROM ...
http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_coalesce
COALESCE(value,...)
Returns the first non-NULL value in the list, or NULL if there are no non-NULL values.
mysql> SELECT COALESCE(NULL,1);
-> 1
mysql> SELECT COALESCE(NULL,NULL,NULL);
-> NULL

Cannot use REPLACE function properly in MySQL

I have an SQL table whose columns are State_Code, School_Code and School_Type.
Sample Data:
State_Code School_Code School_Type
01 01014874 1
01 01018790 2
1 10189900 1
1 10277689 1
*(Note: 1st and 2nd digit of School_Code is the actual state-code while its 3rd and 4th digit is the District_Code of that particular state)*
Now, if I fire this query:
Select COUNT(School_Code) from Table1 where State_Code='1'+0;
It will return the number of schools present in the state, with code '1'; i.e. 4;
But if I want to retrieve the number of schools present in a state DISTRICT-WISE; I would fire this query:
Select State_Code,SUBSTRING(School_Code,3,2) AS District_Code,COUNT(School_Code) AS Number_of_Schools_In_District FROM Table1 GROUP BY State_Code+0;
This query returns me this output:
State_Code District_Code Number_of_Schools_In_District
01 01 2
1 18 1
1 27 1
But the correct output is:
State_Code District_Code Number_of_Schools_In_District
01 01 3
1 02 1
This is due to the incorrect data entered by user in SQL table for column School_Code. State '1' and '01' are actually the same state i.e. '01'. But since School-Code in the 3rd and 4th row does not start with '0', this lead to the incorrect behavior in the output.
So to resolve this problem, I will have to use this logic:
*If number of characters in State_Code is 1 and School_Code does not start with '0', then a '0' should be prefixed to the School_Code value to get the correct District_Code value.*
I tried this:
Select State_Code,SUBSTRING(School_Code,3,2) AS District_Code,COUNT(School_Code) AS Number_of_Schools_In_District FROM Table1 WHERE IF CHARACTER_LENGTH(State_Code)=1 AND School_Code NOT LIKE '0%' THEN REPLACE(School_Code,School_Code,CONCAT(0,School_Code)) END IF GROUP BY State_Code+0, SUBSTRING(School_Code,3,2)+0;
My expectation was if any value in State_code is a single digit and its corresponding School Code does not start with '0', then this will append '0' to it and then calculate district code taking the 3rd and 4th digit. But this does not happen. Still I get the error.
Note: I don't want to update original SQL table values.
Any help will be appreciated. Thanks in Advance!
Try below query
Select State_Code, IF(CHARACTER_LENGTH(State_Code)=1, SUBSTRING(School_Code,2,2), SUBSTRING(School_Code,3,2)) AS District_Code,COUNT(School_Code) AS Number_of_Schools_In_District FROM Table1 WHERE GROUP BY State_Code+0, SUBSTRING(School_Code,3,2)+0;