update varchar field ; Diffrence between using single quotes and with out using - sql

I am using Teradata as database,
Table
sno varchar(10) primary,
number varchar(10)
I was able to update number field like this
update ...
set number = '1'
or
set number = 1
I was able to get correct result in my select query [ had max(number) column ],when I updated second way i.e set number = 1 with out using any cast functions.
using this (i.e set number = '1' ) gives me the wrong result with my select query, with out
using cast functions
can any one explain the difference?
In short , I need difference between
update ...
set number = '1'
or
set number = 1

set number = 1
updates the VARCHAR to '1'
set number = 1
does an automatic Teradata style typecast (right aligned within FORMAT) and sets number to ' 1'.
SELECT (1 (VARCHAR(10))) || '#', FORMAT(1), TYPE(1);
*** Query completed. One row found. 3 columns returned.
*** Total elapsed time was 1 second.
(1||'#') Format(1) Type(1)
----------- ------------------------------ -------------
1# -(3)9 BYTEINT
What do you expect when you query MAX(col)?
For VarChars '9' is greater than '11', if you need numeric comparison you should use a numeric datatype.

Related

How to compose T-SQL WHERE statement to determine whether the specific filter value is a power of 2 collectible from column values

I got SQL Table and column called MultiTypes (int).
MultiyTypes column values are regular numbers. I need to decompose those numbers to power-of-2 collectibles and then to check whether the filter value is one of these collectibles.
How to construct my WHERE Statement properly?
Example:
Column has values:
10 which is (8+2),
25 which is (16+8+1),
17 which is (16+1),
101 which is (64+32+4+1)
I want to build query
SELECT * FROM TABLE
WHERE #FilterValue -???-> MultiTypes
-???-> means - #FilterValue is one of the "power-of-2" collectibles of the iterated value
If filter value is 8, I will get rows with MultiTypes values 10 and 25
If filter value is 1, I will get rows with MultiTypes values 17, 101 and 25
If you want to know if a filter value matches a bit-array in the "1" positions, you use bitwise-and. It looks like:
where MultiType_Column & #filter = #filter
There is no need to decompose the value to specific types.
If you want to know if any of the values are set, then you can use:
where MultiType_Column & #filter <> 0
Generally to test if MultiTypes matches the combination of types, say Type2 + Type4 we do, pseudocode:
(MultiTypes <bit AND> (Type2 <bit OR> Type4)) == (Type2 <bit OR> Type4)
<bit AND> , <bit OR> are sql product specific, check your product's manuals.
Sql-server demo
declare #multitype int = 128 + 16 + 1;
declare #test int = 128 + 1;
declare #test2 int = 64 + 1;
select 'Yes, #multitype includes all types of #test' res
where #multitype & #test = #test;
select 'this will not return any row' res2
where #multitype & #test2 = #test2;
If you want to filter based on the type(k) (2^k), you can use & operator:
SELECT *
FROM table_name
WHERE (MultiyType_Column & (POWER(2,k)) <> 0
The above query gets all rows which have type(k). Fo example if k=3, to get all rows that have type3 you can do the following:
SELECT *
FROM table_name
WHERE (MultiyType_Column & (POWER(2,3)) <> 0
I think this cannot be resolved in a SQL query because the lack of arithmetic operations you can make in the query, my recommendation is to create a procedure and there you can divide the number the times that you need.

Procedure to apply formatting to all rows in a table

I had a SQL procedure that increments through each row and and pads some trailing zeros on values depending on the length of the value after a decimal point. Trying to carry this over to a PSQL environment I realized there was a lot of syntax differences between SQL and PSQL. I managed to make the conversion over time but I am still getting a syntax error and cant figure out why. Can someone help me figure out why this wont run? I am currently running it in PGadmin if that makes any difference.
DO $$
DECLARE
counter integer;
before decimal;
after decimal;
BEGIN
counter := 1;
WHILE counter <> 2 LOOP
before = (select code from table where ID = counter);
after = (SELECT SUBSTRING(code, CHARINDEX('.', code) + 1, LEN(code)) as Afterward from table where ID = counter);
IF before = after
THEN
update table set code = before + '.0000' where ID = counter;
ELSE
IF length(after) = 1 THEN
update table set code = before + '000' where ID = counter;
ELSE IF length(after) = 2 THEN
update table set code = before + '00' where ID = counter;
ELSE IF length(after) = 3 THEN
update table set code = before + '0' where ID = counter;
ELSE
select before;
END IF;
END IF;
counter := counter + 1;
END LOOP
END $$;
Some examples of the input/output of the intended result:
Input 55.5 > Output 55.5000
Input 55 > Output 55.0000
Thanks for your help,
Justin
There is no need for a function or even an update on the table to format values when displaying them.
Assuming the values are in fact numbers stored in a decimal or float column, all you need to do is to apply the to_char() function when retrieving them:
select to_char(code, 'FM999999990.0000')
from data;
This will output 55.5000 or 55.0000
The drawback of the to_char() function is that you need to anticipate the maximum number of digits of that can occur. If you have not enough 9 in the format mask, the output will be something like #.###. But as too many digits in the format mask don't hurt, I usually throw a lot into the format mask.
For more information on formatting functions, please see the manual: https://www.postgresql.org/docs/current/static/functions-formatting.html#FUNCTIONS-FORMATTING-NUMERIC-TABLE
If you insist on storing formatted data, you can use to_char() to update the table:
update the_table
set code = to_char(code::numeric, 'FM999999990.0000');
Casting the value to a number will of course fail if there a non-numeric values in the column.
But again: I strong recommend to store numbers as numbers, not as strings.
If you want to compare this to a user input, it's better to convert the user input to a proper number and compare that to the (number) values stored in the database.
The string matching that you are after doesn't actually require a function either. Using substring() with a regex will do that:
update the_table
set code = code || case length(coalesce(substring(code from '\.[0-9]*$'), ''))
when 4 then '0'
when 3 then '00'
when 2 then '000'
when 1 then '0000'
when 0 then '.0000'
else ''
end
where length(coalesce(substring(code from '\.[0-9]*$'), '')) < 5;
substring(code from '\.[0-9]*$') extracts everything the . followed by numbers that is at the end of the string. So for 55.0 it returns .0 for 55.50 it returns .50 if there is no . in the value, then it returns null that's why the coalesce is needed.
The length of that substring tells us how many digits are present. Depending on that we can then append the necessary number of zeros. The case can be shortened so that not all possible length have to be listed (but it's not simpler):
update the_table
set code = code || case length(coalesce(substring(code from '\.[0-9]*$'), ''))
when 0 then '.0000'
else lpad('0', 5- length(coalesce(substring(code from '\.[0-9]*$'), '')), '0')
end
where length(coalesce(substring(code from '\.[0-9]*$'), '')) < 5;
Another option is to use the position of the . inside the string to calculate the number of 0 that need to be added:
update the_table
set code =
code || case
when strpos(code, '.') = 0 then '0000'
else rpad('0', 4 - (length(code) - strpos(code, '.')), '0')
end
where length(code) - strpos(code, '.') < 4;
Regular expressions are quite expensive not using them will make this faster. The above will however only work if there is always at most one . in the value.
But if you can be sure that every value can be cast to a number, the to_char() method with a cast is definitely the most robust one.
To only process rows where the code columns contains correct numbers, you can use a where clause in the SQL statement:
where code ~ '^[0-9]+(\.[0-9][0-9]?)?$'
To change the column type to numeric:
alter table t alter column code type numeric

Get MAX value if column has a certain format

SQL Server 2008 R2
I have a table similar to this:
Example table:
ID Column
---------------
xxx1234
xxx12345
xxx123456
20150001
I am trying to get a conditional MAX value depending on the value of the column based on whether it meets as certain format. Using the above example, the fourth record, 20150001, represents a "good record" because it contains the current year, and the start of an increment. So, in my table, records that are considered "good" (those subject to the query I am trying to write) have the format "year + increment". The first three, that do not follow this format, should not be conditioned to the query since they don't match this format and should not be subject when computing the max value. Those are bad records. In the above example, the expected result would be "20150002".
The MAX query is simple enough to write, however I am wondering about an approach where I can sanitize the query to only include those records whom meet the particular format, and increment the last four digits (0001 to 0002).
TIA!
You can use the isdate function to filter out ID Columns that do not start with a valid year, and isnumeric to make sure the last 4 characters of the ID Column are valid increments. You also want the len to be 8, given this criteria. You can accomplish all this in the where clause:
-- load test data
declare #Example_Table table(ID_Column varchar(10))
insert into #Example_Table values
('xxx1234'),
('xxx12345'),
('xxx123456'),
('20150001')
-- return max valid ID_Column
select max(ID_Column) as max_ID_Column
from #Example_Table
where isdate(left(ID_Column,4)) = 1
and isnumeric(right(ID_Column,4)) = 1
and len(ID_Column) = 8
-- increment max valid ID_Column
update #Example_Table
set ID_Column = cast(ID_Column as int) + 1
where isdate(left(ID_Column,4)) = 1
and isnumeric(right(ID_Column,4)) = 1
and len(ID_Column) = 8
select * from #Example_Table
ID_Column
----------
xxx1234
xxx12345
xxx123456
20150002
You could use a regular expression to verify a correct year. The second half of the regular expression I taylored to your examples of 0001 and 0002, this could be opened up by adding '[0-9]' for each digit you're expecting.
DECLARE #Sample VARCHAR(30) = '20150001';
SELECT CASE WHEN (#Sample LIKE '[12][09][0-9][0-9]000[12]') THEN 'Yes' ELSE 'No' END;
SELECT
SUBSTRING(#Sample, 1, 4),
SUBSTRING(#Sample, 5, 4),
CASE WHEN (SUBSTRING(#Sample, 1, 4) LIKE '[12][09][0-9]') THEN 'Yes' ELSE 'No' END,
CASE WHEN (SUBSTRING(#Sample, 5, 4) LIKE '[0-9][0-9][0-9][0-9]') THEN 'Yes' ELSE 'No' END;

Converting data for whole table SQL

I'm newbie in SQL and have some questions:
How can I convert columns (in my table with more than 10 000 rows) in my SQL table (I'm using SQL Server 2008):
First column is nvarchar (50) and containing different string values, for e.g. like 20131211142319 and it's a date and time - 2013/12/11 14:23:19. How can I convert this value into date & time and affect this on all rows in this column (more than 10 000).
And also I have column with numbers, all this numbers start from # + number - e.g. #8339274. How can I delete character "#" before all numbers in all rows? Note, that numbers in this column have a different length, from 5 characters to 15 characters.
Thank you in advance.
I couldn't find a more elegant solution for the datetime conversion but here you go:
1. DATETIME conversion
This assumes your value is always in the same format you specified:
Example code for you to run
DECLARE #Value VARCHAR(255) = '20131211142319'
SELECT CONVERT(DATETIME,LEFT(#Value,8) + SPACE(1) + STUFF(STUFF(STUFF(RIGHT(#Value,6), 1, 0, REPLICATE('0', 0)),3,0,':'),6,0,':'))
This splits the field into two sections, the DATE portion LEFT(#Value,8) and then the TIME
STUFF(STUFF(STUFF(RIGHT(#Value,6), 1, 0, REPLICATE('0', 0)),3,0,':'),6,0,':'). The TIME portion is essentially just adding in the colon where applicable (see STUFF on MSDN)so that it returns a value such as:
20131211 14:23:19 This makes it applicable to directly convert to a DATETIME.
2. Removing the # from the numbers
Example code for you to run
DECLARE #ValueNumber VARCHAR(255) = '#8339274'
SELECT SUBSTRING(#ValueNumber,2,LEN(#ValueNumber))
The above statement will take your number and only return data from the 2nd value onwards, excluding the #. See SUBSTRING on MSDN
To make this run on your table, just replace my variable names in the SELECT statement with your column names.
Example using the above on columns in a table:
SELECT CONVERT(DATETIME,LEFT([DATECOLUMNNAME],8) +
SPACE(1) + STUFF(STUFF(STUFF(RIGHT([DATECOLUMNNAME],6),
1, 0,REPLICATE('0',0)),3,0,':'),6,0,':')) AS [Date],
SUBSTRING([NUMBERCOLUMNNAME],2,LEN([NUMBERCOLUMNNAME])) AS [Number]
FROM [TABLENAME]
Replace [DATECOLUMNNAME] with the name of the column that holds your datetime value. Replace the [NUMBERCOLUMNNAME] with the name of the column that holds your number with the #.
Then finally replace [TABLENAME] with your table name that contains those columns.
try this : below answer is also correct
declare #a nvarchar(50)
set #a='20131211142319'
select cast(left(#a,4)+'/'+substring(#a,5,2)+'/'+substring(#a,7,2)+ ' '+ substring(#a,9,2)+':'+substring(#a,11,2)+':' +right(#a,2) as datetime)
output's this --2013-12-11 14:23:19.000
declare #a nvarchar(10)
set #a='#1234567'
select replace(#a,'#','')
outputs this--1234567

Sorting a varchar column as integer value in Oracle query

I have a column DOOR which is a VARCHAR2 in a Table ADDRESS. I want to sort the column DOOR.
DOOR contains only two digits and no - sign
currently when I use the query
select sname, door, zip from address a order by door
I get the following result:
a
b
1
10
11
2
3
31
But I want the result to look like this:
a
b
1
2
3
10
11
31
I tried to convert DOOT into a numeric value using to_number as
select sname, to_number(door) dnr, zip from address a order by dnr
but it is giving me an error ORA-01722.
You can do this with the logic in the order by:
order by (case when regexp_like(door, '^[0-9]*$') = 0 then 1 else 0 end) desc,
(case when regexp_like(door, '^[0-9]*$') = 0 then door end),
length(door),
door
This first puts the non-numeric values first. The second clauses sorts these alphabetically. The third and fourth are for numbers. By sorting for the length before the value, you will get the numbers in order.
ORA-01722 error coming because of value 'a' ,'b',
Go for custom function which will take varchar and return number to convert , use custom function in order by clause of your query.
CREATE OR REPLACE FUNCTION tonumber (no_str varchar2)
RETURN number IS
num number := 0;
BEGIN
RETURN to_number(no_str);
EXCEPTION -- exception handlers begin A < B < 1 < 2
WHEN value_error THEN -- handles all other errors
dbms_output.put_line('in other exception catch.');
CASE
WHEN ( upper(no_str) = 'B' ) THEN return -1;
WHEN ( upper(no_str) ='A') THEN return -2;
ELSE return -999;
END CASE;
END;
Add when condition as in required. now assumed it can have only A B. for rest it will return default.
(This approach assumes that there aren't any mixed values like "234abc567".)
So, going old school...just 0-pad the strings to the maximum length of the column so that they'll sort properly as characters. But, to get the "non-numeric" values to sort first, nullify non-numeric values, put the NULLs first and padded values after that.
select door
from address
order by case when replace(translate(door, '012345679', '0000000000'), '0', '') is null
then lpad(door, 10, '0') -- value was entirely made of digits (change the 10 to the max width of the column)
else null
end nulls first
, door -- sorting within the group of NULL rows generated in the previous expression.
use the below query
SELECT PUMP_NAME
FROM MASTER.PUMPS
ORDER BY LPAD(PUMP_NAME, 10);