array column is returned as as string instead of array - sql

I have array column in the pgsql db, when i select the column
SELECT scopes FROM users
it Returns data like this '{"admin","agent"}' instead of ["admin","agent"].
Is there a way to do this.
I expect it to be ["admin","agent"] and not '{"admin","agent"}'

Seems like it matches the common PostgreSql syntax for arrays, ex. a column can also be defined as an array to introduce another dimension.
Example input
INSERT INTO sal_emp
VALUES ('Bill',
'{10000, 10000, 10000, 10000}',
'{{"meeting", "lunch"}, {"training", "presentation"}}');
INSERT INTO sal_emp
VALUES ('Carol',
'{20000, 25000, 25000, 25000}',
'{{"breakfast", "consulting"}, {"meeting", "lunch"}}');
Becomes
SELECT * FROM sal_emp;
name | pay_by_quarter | schedule
-------+---------------------------+-------------------------------------------
Bill | {10000,10000,10000,10000} | {{meeting,lunch},{training,presentation}}
Carol | {20000,25000,25000,25000} | {{breakfast,consulting},{meeting,lunch}}
(2 rows)
From https://www.postgresql.org/docs/9.1/arrays.html
Is there a way to do this.
If the output isn't for ingestion by PostgreSql, then String substitution is an option.
SELECT REPLACE(REPLACE('<your input>', '{', '['), '}', ']') ..;

Related

SQL change number format to custom "0000000000000000" from any integer number in SELECT statement

I am trying write a SQL SELECT statement in Snowflake where I need to select a column 'xyz' form table 'a'(select xyz from a). Although, the number in column xyz is formatted into 6,7,8 digits and I want to convert that in select statement itself to 16 digits with leading 0's. I used concat() but since the column contains either 6,7,8 digits I am not able to format it into max 16 digits with leading 0's.
Note: I need it to be in select statement as I cant update the column in the database to 16 digit format.
Example
Input: 123456, 1234567, 12345678
Output should be: 0000000000123456, 0000000001234567, 0000000012345678
Can someone help me out here please. Thanks!
Using TO_VARCHAR with format provided:
SELECT TO_VARCHAR(12345, '0000000000000000');
WITH cte(c) AS (
SELECT * FROM (VALUES (123456), (1234567),(12345678))
)
SELECT c, TO_VARCHAR(c, '0000000000000000') AS result
FROM cte;
Output:
with data(c) as (
select * FROM (
values (12345), (293848238), (284), (239432043223432)
)
)
select lpad(c, 16, '0') as c from data;
+------------------+
| C |
|------------------|
| 0000000000012345 |
| 0000000293848238 |
| 0000000000000284 |
| 0239432043223432 |
+------------------+
If you want dynamic width, or to stick to a form of CONCAT then here are some extra ways.
SELECT column1,
TO_CHAR(column1, '0000000000000000') as fixed_width,
TO_CHAR(column1, REPEAT('0', 16)) as dynamic_width,
RIGHT(REPEAT('0', 16) || column1::text, 16) as another_way,
LPAD(column1, 16, '0')
FROM VALUES
(123456),
(1234567),
(12345678),
(12345.678),
(1234567890123456789)
COLUMN1
FIXED_WIDTH
DYNAMIC_WIDTH
ANOTHER_WAY
LPAD(COLUMN1, 16, '0')
123,456
0000000000123456
0000000000123456
000000123456.000
000000123456.000
1,234,567
0000000001234567
0000000001234567
000001234567.000
000001234567.000
12,345,678
0000000012345678
0000000012345678
000012345678.000
000012345678.000
12,345.678
0000000000012346
0000000000012346
000000012345.678
000000012345.678
1,234,567,890,123,456,789
################
################
890123456789
1234567890123456
the two TO_CHAR/TO_VARCHAR methods don't deal with floating results, where-as the RIGHT version does. But that doesn't handle values that are larger the 16 DP
Using you SQL
So using the SQL you put in your comment, and then using 2 CTE's for fake out some data AND using one of the many answers to your question, we get:
WITH "abc" as (
SELECT * FROM VALUES
(123456),
(1234567)
t(ki)
), "xyz" as (
SELECT * FROM VALUES
(6, 123456),
(7, 1234567)
t(ki_value, piv_id)
)
SELECT DISTINCT
y.kI,
yo.ki_value,
yo.piv_num
FROM "abc" as y
left join (
select
ki_value,
LPAD(piv_id, 16, '0') as piv_num
--concat('0000000000',piv_id) as piv_num
from "xyz"
) as yo on y.KI = yo.piv_num
WHERE y.KI in (123456,1234567)
this gives:
KI |KI_VALUE |PIV_NUM
--|--|--
123,456 |6 |0000000000123456
1,234,567 |7 |0000000001234567
So this "works" but as your SQL is written, it's really bad. The WHERE statement is checking the values are numbers and are in the range, why on earth are you want to string pad numbers as TEXT with however many number of zeros in front, because you should leave them as numbers.

How to get values from table using multiple ID's in single column in SQL?

I am working in an existing stored procedure. In that, they fetch some data using select Queries. Now I am trying to fetch some more data. The problems I have is, the column I am using to fetch data from another table have more than one Id saved in with comma(,) in between.
Procedure with 2 tables as Tbl_av,Tbl_aa.
I have one column in Tbl_av as processId that is in Varchar and it stores data like 1,2,4 etc.. (that is, it stores the Id of the Process).
Now I am trying to get the Process Name from one more table Tbl_Process using this ProcessId under the table Tbl_av. In that table, the process Id is Unique one and it is in INTEGER.
Tbl_Process as follows:
Tbl_av as follows:
I have procedure few part as follows:
SET #strQuery= 'SELECT isnull(av.ID, 0) AS ID
,isnull(ItemNumber, '''') AS Number
,isnull(av.ItemName,'''') as Item Name
,av.Description
,(select top 1 Name from TBL_Names where Id =aa.id)as Person Incharge from Tbl_AV av, Tbl_aa aa WHERE av.S_number = aa.S_number'
Now what I am trying to do is , I need to fetch that Process names from Tbl_Process using this Tbl_av ProcessId's inside the procedure.
I dont know how to achieve it, as it both columns are in different datatype and more than 1 id is saved in one column
NOTE: We can simply achieve this as 'SELECT Process_Name from Tbl_Process WHERE Id in (av.ProcessId)' - But doing this ,displays data in column format..
I want data to be selected as if Id is 1,2 means I want my output as Process_Name = Item 1,Item 2.
Kindly help.
Two tricks are needed. One is joining based on a comma-separated list of IDs. That can easily be done poorly resulting in unwanted matches such as 1 and 2 matching 12. The article Stephen Jennings referenced has some good reliable solutions.
The second is concatenating a collection of results into a single string. For recent versions of SQL Server, the STRING_AGG is the preferred solution. For older versions (such as 2014) the most common method is the "FOR XML" trick.
I've combined the two techniques below.
DECLARE #Tbl_Process TABLE(ID INT, process_Name VARCHAR(100))
INSERT #Tbl_Process
VALUES (1, 'Item 1'), (2, 'Item 2'), (3, 'Item 3'), (4, 'Item 4'), (5, 'Item 5'), (12, 'Item 12')
DECLARE #Tbl_av TABLE(ID INT, ProcessId VARCHAR(100))
INSERT #Tbl_av
VALUES (1, '1,3,5'), (2, '2,4'), (3, '1,2,3'), (4, '1,5'), (5, ''), (6, '3,4,12')
SELECT AV.*, PL.*
FROM #Tbl_av AV
CROSS APPLY (
SELECT ISNULL(STUFF(
(
SELECT ',' + P.process_Name
FROM #Tbl_Process P
--(bad) WHERE CHARINDEX(CONVERT(VARCHAR, P.ID), AV.ProcessId) > 0
WHERE ',' + AV.ProcessId + ',' LIKE '%,' + CONVERT(VARCHAR, P.ID) + ',%'
ORDER BY P.ID -- alternately ORDER BY P.process_Name
FOR XML PATH(''), TYPE
).value('text()[1]','nvarchar(max)')
, 1, 1, '')
, '(none)')
AS Process_Names
) PL
Results
ID
ProcessId
Process_Names
1
1,3,5
Item 1,Item 3,Item 5
2
2,4
Item 2,Item 4
3
1,2,3
Item 1,Item 2,Item 3
4
1,5
Item 1,Item 5
5
(none)
6
3,4,12
Item 3,Item 4,Item 12
The FOR XML PATH('') trick causes the results to concatenate into a single XML string with no tags. The , TYPE together with .value('text()[1]','nvarchar(max)') safely extracts the resulting text, undoing any XML specific text encodings such as &, <, or >. The STUFF() removes the leading comma, and the ISNULL() provides a default if there are no values.
See How Stuff and 'For Xml Path' work in SQL Server? for more on how the FOR XML trick works.
If you prefer comma-space list separators, you can update the ',', but will also need to adjust the STUFF to strip two characters instead of one.
The contents of the cross apply could be moved directly into the SELECT clause, but as a matter of style, the CROSS APPLY allows separation of complex logic from the rest of the query.

How to replace rows containing alphabets and special characters with Blank spaces in snowflake

I have a column "A" which contains numbers for example- 0001, 0002, 0003
the same column "A" also contains some alphabets and special characters in some of the rows for example - connn, cco*jjj, hhhhhh11111 etc.
I want to replace these alphabets and special characters rows with blank values and only want to keep the rows containing the number.
which regex expression I can use here?
If you want to extract numbers from these values (even if they end or start with non digits), you may use something like this:
create table testing ( A varchar ) as select *
from values ('123'),('aaa123'),('3343'),('aaaa');
select REGEXP_SUBSTR( A, '\\D*(\\d+)\\D*', 1, 1, 'e', 1 ) res
from testing;
+------+
| RES |
+------+
| 123 |
| 123 |
| 3343 |
| NULL |
+------+
I understand that you want to set to null all values that do not contain digits only.
If so, you can use try_to_decimal():
update mytable
set a = null
where a try_to_decimal(a) is null
Or a regexp match:
update mytable
set a = null
where a rlike '[^0-9]'

Convert array to rows in Postgres

If I have something like this in SQL statement ('A','B','C'), how do I convert it into a column with multiple rows like this
col
---
A
B
C
I cannot change the way that string is created (as it is injected into SQL query from external program). For example, I cannot make it as ['A','B','C'] (replace with square brackets). I could wrap anything around it though like [('A','B','C')] or whatever.
Any help?
UPDATE 1
I have PostgreSQL 8.4.20
You could create an ARRAY from VALUES and then unnest it:
SELECT
unnest(ARRAY[col_a, col_b, col_c])
FROM
(VALUES('A','B','C')) AS x(col_a, col_b, col_c)
Result:
| unnest |
|--------|
| A |
| B |
| C |
Edit: you could also tweak jspcal's answer by using dollar quotes ($$) like this so you can concatenate your string into the SQL statement:
SELECT * FROM regexp_split_to_table(
regexp_replace(
$$('A','B','C','D','foo')$$,
'^\(''|''\)+', '', 'g'),
''','''
);
The built-in regexp_split_to_table function will do this for you. Since you plan to inject it directly without escaping, use $$ (dollar quoting) from thibautg's answer.
select * from regexp_split_to_table(
regexp_replace($$('A','B','C')$$, '^\(''|''\)+', '', 'g'),
''','''
);

sql, strategies to find out string contains certain texts

I want to select any data that contains 800, 805, 888... (there are 8 pattern texts) in the column.
Do I have to use like statement 8 times for each one or is there a faster way?
Example:
SELECT * FROM caller,
WHERE id LIKE '%805%' OR id LIKE'%800' OR ... ;
(PS. I am not allowed to create another table, just using sql queries.)
LIKE is for strings, not for numbers. Assuming id is actually a number, you first need to cast it to a string in order to be able to apply a LIKE condition on it.
But once you do that, you can use an array for that:
SELECT *
FROM caller
WHERE id::text LIKE ANY (array['%805%', '%800', '81%']);
Use any() with an array of searched items:
with test(id, col) as (
values
(1, 'x800'),
(2, 'x855'),
(3, 'x900'),
(4, 'x920')
)
select *
from test
where col like any(array['%800', '%855'])
id | col
----+------
1 | x800
2 | x855
(2 rows)
This is shorter to write but not faster to execute I think.