How to add a leading zero when the length of the column is unknown? - postgresql

How can I add a leading zero to a varchar column in the table and I don't know the length of the column. If the column is not null, then I should add a leading zero.
Examples:
345 - output should be 0345
4567 - output should be 04567
I tried:
SELECT lpad(column1,WHAT TO SPECIFY HERE?, '0')
from table_name;
I will run an update query after I get this.

You may be overthinking this. Use plain concatenation:
SELECT '0' || column1 AS padded_col1 FROM table_name;
If the column is NULL, nothing happens: concatenating anything to NULL returns NULL.
In particular, don't use concat(). You would get '0' for NULL columns, which you do not want.
If you also have empty strings (''), you may need to do more, depending on what you want.
And since you mentioned your plan to updated the table: Consider not doing this, you are adding noise, that could be added for display with the simple expression. A VIEW might come in handy for this.
If all your varchar values are in fact valid numbers, use an appropriate numeric data type instead and format for display with the same expression as above. The concatenation automatically produces a text result.
If circumstances should force your hand and you need to update anyway, consider this:
UPDATE table_name
SET column1 = '0' || column1
WHERE column1 IS DISTINCT FROM '0' || column1;
The added WHERE clause to avoid empty updates. Compare:
How do I (or can I) SELECT DISTINCT on multiple columns?

try concat instead?..
SELECT concat(0::text,column1) from table_name;

Related

Check if set of columns have scientific value

I need to check if 4 columns (varchar) have at least 1 row with scientific connotation values (E+)
I'm doing this for a single column:
declare
_ean int;
begin
t_query = '
select count(*) from mytable where trim_to_null(myfield) is not null and (trim_to_null(myfield) ilike '%E+%');';
execute t_query into _ean;
IF _ean != 0 THEN
RAISE NOTICE 'EAN has a scientific connotation, please review the file';
return 'Error The file contains % EAN with scientific connotation';
END IF;
return null;
It works ok for this one column but now I need to also check 4 more columns and I need to tell on which column the scientific connotation was found, I could do this by multiples "IF" to check on each column but I bet there's a better way to do it in one sentence, and return the column/s name which had the scientific connotation.
As stated in the comments, you don't need dynamic SQL for that.
Also, storing numbers as strings is really bad practice – your queries get more complicated, and somebody could store non-numbers as well.
All that said, I thing your query ignored the fact that scientific notation could also be 1e-7 or 1e4.
So I think the query should contain
WHERE trim_to_null(myfield) ILIKE '%E%'
or, if you want to check the number for correctness, something like
WHERE trim_to_null(myfield) ~ '^[+-]?[0-9]+(\.[0-9]*)?[eE][+-]?[0-9]+$'
But to your original question:
You could run
SELECT id, col1_is_ean, col2_is_ean, col3_is_ean
FROM (SELECT id,
col1 ILIKE '%E%' AS col1_is_ean,
col2 ILIKE '%E%' AS col2_is_ean,
col3 ILIKE '%E%' AS col3_is_ean
FROM mytable) AS q
WHERE col1_is_ean OR col2_is_ean OR col3_is_ean;

Casting rows to arrays in PostgreSQL

I need to query a table as in
SELECT *
FROM table_schema.table_name
only each row needs to be a TEXT[] with array values corresponding to column values casted to TEXT coming in the same order as in SELECT * so assuming the table has columns a, b and c I need the result to look like
SELECT ARRAY[a::TEXT, b::TEXT, c::TEXT]
FROM table_schema.table_name
only it shouldn't explicitly list columns by name. Ideally it should look like
SELECT as_text_array(a)
FROM table_schema.table_name AS a
The best I came up with looks ugly and relies on "hstore" extension
WITH columnz AS ( -- get ordered column name array
SELECT array_agg(attname::TEXT ORDER BY attnum) AS column_name_array
FROM pg_attribute
WHERE attrelid = 'table_schema.table_name'::regclass AND attnum > 0 AND NOT attisdropped
)
SELECT hstore(a)->(SELECT column_name_array FROM columnz)
FROM table_schema.table_name AS a
I am having a feeling there must be a simpler way to achieve that
UPDATE 1
Another query that achieves the same result but arguably as ugly and inefficient as the first one is inspired by the answer by #bspates. It may be even less efficient but doesn't rely on extensions
SELECT r.text_array
FROM table_schema.table_name AS a
INNER JOIN LATERAL ( -- parse ROW::TEXT presentation of a row
SELECT array_agg(COALESCE(replace(val[1], '""', '"'), NULLIF(val[2], ''))) AS text_array
FROM regexp_matches(a::text, -- parse double-quoted and simple values separated by commas
'(?<=\A\(|,) (?: "( (?:[^"]|"")* )" | ([^,"]*) ) (?=,|\)\Z)', 'xg') AS t(val)
) AS r ON TRUE
It is still far from ideal
UPDATE 2
I tested all 3 options existing at the moment
Using JSON. It doesn't rely on any extensions, it is short to write, easy to understand and the speed is ok.
Using hstore. This alternative is the fastest (>10 times faster than JSON approach on a 100K dataset) but requires an extension. hstore in general is very handy extension to have through.
Using regex to parse TEXT presentation of a ROW. This option is really slow.
A somewhat ugly hack is to convert the row to a JSON value, then unnest the values and aggregate it back to an array:
select array(select (json_each_text(to_json(t))).value) as row_value
from some_table t
Which is to some extent the same as your hstore hack.
If the order of the columns is important, then using json and with ordinality can be used to keep that:
select array(select val
from json_each_text(to_json(t)) with ordinality as t(k,val,idx)
order by idx)
from the_table t
The easiest (read hacky-est) way I can think of is convert to a string first then parse that string into an array. Like so:
SELECT string_to_array(table_name::text, ',') FROM table_name
BUT depending on the size and type of the data in the table, this could perform very badly.

Postgresql parse string values to integers

Is there a way in Postgres I can parse string values to integers? Basically I'm trying to query one table (let's call it table_one) using values from another table (table_two) in a character varying column.
Say SELECT char_column FROM table_two results in "2,4,6,8", I'd like to use this result in a second query as;
SELECT column FROM table_one WHERE some_id IN (2,4,6,8)
How can I get the string "2,4,6,8" to values 2,4,6,8 so as to be able to use it in the second query?
I've tried casting and to_number functions to no success.
SELECT column
FROM table
WHERE other_column = ANY(string_to_array('2,4,6,8', ',')::INT[])
Please try this:
SELECT column FROM table WHERE other_column IN (
SELECT NULLIF(i,'')::int
FROM regexp_split_to_tables('2,4,6,8',',') t(i)
)
Explanation:
The part regexp_split_to_tables('2,4,6,8',',') will split the string into a table. Then you cast it into integer.
Hopefully it will help you.

Inserting commas into values

I have a field, let's call it total_sales where the value it returns is 3621731641
I would like to convert that so it has a thousand separator commas inserted into it. So it would ultimately return as 3,621,731,641
I've looked through the Redshift documentation and have not been able to find anything.
Similar to following query should work for you.
select to_char(<columnname>,'999,999,999,999') from table1;
Make sure to put Maximum size in while specifying pattern into second parameter.
It should not give you $ if don't specify 'l' in second parameter like below.
select to_char(<columnname>,'l999,999,999,999') from table1;
Money format: select '$'||trim(to_char(1000000000.555,'999G999G999G999.99'))
select to_char(<columnname>,'999,999,999,999') from table1;
or
select to_char(<columnname>,'999G999G999G999') from table1;

ltrim(rtrim(x)) leave blanks on rtl content - anyone knows on a work around?

i have a table [Company] with a column [Address3] defined as varchar(50)
i can not control the values entered into that table - but i need to extract the values without leading and trailing spaces. i perform the following query:
SELECT DISTINCT RTRIM(LTRIM([Address3])) Address3 FROM [Company] ORDER BY Address3
the column contain both rtl and ltr values
most of the data retrieved is retrieved correctly - but SOME (not all) RTL values are returned with leading and or trailing spaces
i attempted to perform the following query:
SELECT DISTINCT ltrim(rTRIM(ltrim(rTRIM([Address3])))) c, ltrim(rTRIM([Address3])) b, [Address3] a, rtrim(LTRIM([Address3])) Address3 FROM [Company] ORDER BY Address3
but it returned the same problem on all columns - anyone has any idea what could cause it?
The rows that return with extraneous spaces might have a kind of space or invisible character the trim functions don't know about. The documentation doesn't even mention what is considered "a blank" (pretty damn sloppy if you ask me). Try taking one of those rows and looking at the characters one by one to see what character they are.
since you are using varchar, just do this to get the ascii code of all the bad characters
--identify the bad character
SELECT
COUNT(*) AS CountOf
,'>'+RIGHT(LTRIM(RTRIM(Address3)),1)+'<' AS LastChar_Display
,ASCII(RIGHT(LTRIM(RTRIM(Address3)),1)) AS LastChar_ASCII
FROM Company
GROUP BY RIGHT(LTRIM(RTRIM(Address3)),1)
ORDER BY 3 ASC
do a one time fix to data to remove the bogus character, where xxxx is the ASCII value identified in the previous select:
--only one bad character found in previous query
UPDATE Company
SET Address3=REPLACE(Address3,CHAR(xxxx),'')
--multiple different bad characters found by previous query
UPDATE Company
SET Address3=REPLACE(REPLACE(Address3,CHAR(xxxx1),''),char(xxxx2),'')
if you have bogus chars in your data remove them from the data and not each time you select the data. you WILL have to add this REPLACE logic to all INSERTS and UPDATES on this column, to keep any new data from having the bogus characters.
If you can't alter the data, you can just select it this way:
SELECT
LTRIM(RTRIM(REPLACE(Address3,CHAR(xxxx),'')))
,LTRIM(RTRIM(REPLACE(REPLACE(Address3,CHAR(xxxx1),''),char(xxxx2),'')))
...