Postgresql put strings inside quotes with array_to_string - arrays

In select I have used array_to_string like this (example)
array_to_string(array_agg(tag_name),';') tag_names
I got resulting string "tag1;tag2;tag3;..." but I would like to get resulting string as "'tag1';'tag2';'tag3';...".
How can I do this in Postgres?

Use the functions string_agg() and format() with the %L placeholder, which quotes the argument value as an SQL literal.
with my_table(tag_name) as (
values
('tag1'),
('tag2'),
('tag3')
)
select string_agg(format('%L', tag_name), ';' order by tag_name) tag_names
from my_table;
tag_names
----------------------
'tag1';'tag2';'tag3'
(1 row)
Alternatively, format('%L', tag_name) may be replaced with quote_nullable(tag_name).

Or your can use unnest, format, array_agg and array_to_string in one request like this :
select array_to_string(t.tag, ',')
from (
select array_agg(format('%L', t.tag)) as tag
from (
select unnest(tag_name) as tag
) t
) t;

Or use
array_to_string(array_agg(''''||tag_name||''''),';') tag_names
or even simpler (thanks for the commenting :) )
string_agg(''''||tag_name||''''),';') tag_names
Note:
When dealing with multiple-argument aggregate functions, note that the
ORDER BY clause goes after all the aggregate arguments. For example,
write this:
SELECT string_agg(a, ',' ORDER BY a) FROM table;
not this:
SELECT string_agg(a ORDER BY a, ',') FROM table; -- incorrect
See https://www.postgresql.org/docs/current/static/sql-expressions.html#SYNTAX-AGGREGATES

You can use string_agg() function with '''; ''' so it will be like
SELECT string_agg(tag_name, '''; ''') from my_table

Related

Select subset of delimited string field

My table has a field with data formatted like this:
Term 1~Term 2~Term 3~Term 4~Term 5~Term 6~
All non-blank values contain 6 tilde-separated strings, which may be several words long.
I need to extract the last 2 substrings from this field as part of a query.I'm not interested in splitting the data into multiple records, and I don't have permissions to create a stored procedure.
Thanks in advance for any advice.
DECLARE #Term VARCHAR(100)
SELECT #Term = 'abc~def~ghi~jkl~mno~pqr~'
SELECT RIGHT(#Term, CHARINDEX('~',REVERSE(#Term),CHARINDEX('~',REVERSE(#Term),2)+1)-1)
That will give the last two terms with ~ intact. Note you can wrap REPLACE() around that to put something other than the tilde in there.
another way to do is this.. use string_split (in 2016) or an equivalent UDF that can be found elsewhere.. to split the string
declare #term varchar(100) = 'abc~def~ghi~jkl~mno~pqr~'
; with mycte as (
select
value as string_value
, row_number() over (order by (select 1000) )as row_num
from string_split(#term,'~'))
select top 2 string_value
from mycte
where string_value<>''
order by row_num desc

MS SQL Server - Use Calculated Field of SELECT statement

i would like to ask you if there is a statement to use calculated fields of the same SELECT-statement:
For example:
Table Test:
Machine Amount Value
500 20 20
SELECT Machine,
Amount*Value AS TestFormula
TestFormula*12 AS TestFormulaYear
FROM Test
What is the correct statement to reuse this calculated field?
Thanks in advance,
Kevin
In sql server at least, you can do it with a subquery:
SELECT Machine
, TestFormula
, TestFormula*12 AS TestFormulaYear
FROM (
SELECT Machine
, Amount*Value AS TestFormula
FROM Test
) T
For the simple example you showed us, I would just recommend repeating the expression
SELECT
Machine,
Amount*Value AS TestFormula,
Amount*Value*12 AS TestFormulaYear
FROM Test;
Other answers have already shown how you can use a subquery to truly reuse the column, but that is not very performant compared to what I wrote above.
You can use a common-table expression (CTE) to reuse the value:
WITH formula AS (
SELECT Machine,
Amount*Value AS TestFormula
FROM Test
)
SELECT Machine,
TestFormula
TestFormula*12 AS TestFormulaYear
FROM formula;
If the batch with the CTE contains multiple statements, the preceding statement must be terminated with a semicolon.
Assuming this is T-SQL:
You can't reference the alias of a column in the SELECT statement, no. If you look at SELECT (Transact-SQL) you'll note that the SELECT is the 8th part of the query to be processed. This means only ORDER BY is going to be able to reference a column's alias.
If you need to do further calculations on a calculated value you need to use a CTE, subquery, or redeclare the calculation. For example:
Repeated calculation:
SELECT [Column] * 10 As Expression,
[Column] * 10 * 5 AS Expression2
FROM [Table];
CTE:
WITH Formula AS(
SELECT [Column] * 10 As Expression
FROM [Table])
SELECT Expression,
Expression * 5 AS Expression2
FROM Formula;
Sub Query:
SELECT Expression,
Expression * 5 AS Expression2
FROM (SELECT [Column] * 10 As Expression
FROM [Table]) Formula;
If you are looking to set up a Statement so that when formulas are changed many columns will be updated, I suppose you could declare the formulas and use Dynamic SQL. There can be an advantage to this if you want to be sure that lots of columns are updated correctly:
Declare #TestFormula as nvarchar(100) = '([Amount]*[Value])'
Declare #TestFormulaYear as nvarchar(100) = '(12*' + #TestFormula + ')'
declare #sql as nvarchar(max)
set #sql = 'SELECT [Machine], ' + #TestFormula + ' AS TestFormula, ' + #TestFormulaYear + ' AS TestFormulaYear
FROM (values(500, 20, 20)) a([Machine], [Amount], [Value])'
exec(#sql)

select from SQL table with concatenation of two string columns

Issue
I want to write a query that will select all from a table where my string value is equal to two columns concatenated together.
This is plain English version:
#MYSTRING varchar(50)
SELECT ALL FROM [FFLOCNP] WHERE COLUMN1 + COLUMN2 = #MYSTRING
I have tried to use the COALESCE but i have never used this before and it is returning me an error:
#CODE varchar(50)
SELECT * FROM [dbo].[FFLOCNP] WHERE COALESCE([LOCTRY], '') || COALESCE([LOCLCN], '') = #CODE
you have to use ISNULL for this.
Use below query may be it helps you.
SELECT * FROM [FFLOCNP] WHERE ISNULL(COLUMN1,'') + ISNULL(COLUMN2,'') = #MYSTRING
Be careful, when using ISNULL instead of COALESCE. ISNULL limits the returned value to the datatype of the first input parameter. In the given example column V1 will be implicitly defined with nvarchar(1), because the longest text in column V1 consists of only one character. ISNULL(V1, [param2]) will therefor return always a one character long string, regardless of the length of the second parameter. In your case ISNULL would work, if you wanted to replace a NULL with an empty string. If you wanted to replace a NULL with a longer string then you MUST use COALESCE instead of ISNULL. COALESCE returns the full string in parameter 2 regardless of the datatype of parameter 1. Apart from this COALESCE is standard SQL whereas ISNULL is a flavor of SQL-Server. Standard SQL should be preferred to T-SQL flavor to get more portable code.
WITH CTE_SRC AS
(
SELECT
[V1]
,[V2]
FROM
(VALUES
(N'A', N'BB')
,(NULL, N'BB')
,(N'A', NULL)
) T([V1],[V2])
)
SELECT
ISNULL([V1], '1234') AS [ISNULL]
,COALESCE([V1], '123') AS [COALESCE]
FROM
CTE_SRC
Result
ISNULL COALESCE
------ --------
A A
1 123
A A

SQL where in clause with array of array

I know that the WHERE ... IN ... clause allows to select all values of a field that exists in a given array. How do I expand this idea to a group of fields? Something like:
SELECT * FROM table_name
WHERE (First, Last) IN ((Adam, Scott), (Betty, Johnson), (Cathy, Wyatt))
Or any other method that allows the same result.
The syntax in the question is valid in some other RDBMSs but not SQL Server. If on 2008+ you can use
SELECT *
FROM table_name t
WHERE EXISTS(SELECT *
FROM (VALUES ('Adam', 'Scott'),
('Betty','Johnson'),
('Cathy','Wyatt')
) v(first, last)
WHERE t.first = v.first AND t.last = f.last
It depends on what you are after really. If you just want to combine 2 fields then you can use something like this:
SELECT * FROM table_name
WHERE First+','+Last IN ('Adam, Scott', 'Betty, Johnson', 'Cathy, Wyatt')
SELECT * FROM Table_name
WHERE First IN ('Adam', 'Betty', 'Cathy')
AND Last IN ('Scott', 'Johnson', 'Wyatt')

PostgreSQL - join statement duplicate row data combine to single row [duplicate]

I am looking for a way to concatenate the strings of a field within a group by query. So for example, I have a table:
ID COMPANY_ID EMPLOYEE
1 1 Anna
2 1 Bill
3 2 Carol
4 2 Dave
and I wanted to group by company_id to get something like:
COMPANY_ID EMPLOYEE
1 Anna, Bill
2 Carol, Dave
There is a built-in function in mySQL to do this group_concat
PostgreSQL 9.0 or later:
Modern Postgres (since 2010) has the string_agg(expression, delimiter) function which will do exactly what the asker was looking for:
SELECT company_id, string_agg(employee, ', ')
FROM mytable
GROUP BY company_id;
Postgres 9 also added the ability to specify an ORDER BY clause in any aggregate expression; otherwise you have to order all your results or deal with an undefined order. So you can now write:
SELECT company_id, string_agg(employee, ', ' ORDER BY employee)
FROM mytable
GROUP BY company_id;
PostgreSQL 8.4.x:
PostgreSQL 8.4 (in 2009) introduced the aggregate function array_agg(expression) which collects the values in an array. Then array_to_string() can be used to give the desired result:
SELECT company_id, array_to_string(array_agg(employee), ', ')
FROM mytable
GROUP BY company_id;
PostgreSQL 8.3.x and older:
When this question was originally posed, there was no built-in aggregate function to concatenate strings. The simplest custom implementation (suggested by Vajda Gabo in this mailing list post, among many others) is to use the built-in textcat function (which lies behind the || operator):
CREATE AGGREGATE textcat_all(
basetype = text,
sfunc = textcat,
stype = text,
initcond = ''
);
Here is the CREATE AGGREGATE documentation.
This simply glues all the strings together, with no separator. In order to get a ", " inserted in between them without having it at the end, you might want to make your own concatenation function and substitute it for the "textcat" above. Here is one I put together and tested on 8.3.12:
CREATE FUNCTION commacat(acc text, instr text) RETURNS text AS $$
BEGIN
IF acc IS NULL OR acc = '' THEN
RETURN instr;
ELSE
RETURN acc || ', ' || instr;
END IF;
END;
$$ LANGUAGE plpgsql;
This version will output a comma even if the value in the row is null or empty, so you get output like this:
a, b, c, , e, , g
If you would prefer to remove extra commas to output this:
a, b, c, e, g
Then add an ELSIF check to the function like this:
CREATE FUNCTION commacat_ignore_nulls(acc text, instr text) RETURNS text AS $$
BEGIN
IF acc IS NULL OR acc = '' THEN
RETURN instr;
ELSIF instr IS NULL OR instr = '' THEN
RETURN acc;
ELSE
RETURN acc || ', ' || instr;
END IF;
END;
$$ LANGUAGE plpgsql;
How about using Postgres built-in array functions? At least on 8.4 this works out of the box:
SELECT company_id, array_to_string(array_agg(employee), ',')
FROM mytable
GROUP BY company_id;
As from PostgreSQL 9.0 you can use the aggregate function called string_agg. Your new SQL should look something like this: SELECT company_id, string_agg(employee, ', ')
FROM mytable
GROUP BY company_id;
I claim no credit for the answer because I found it after some searching:
What I didn't know is that PostgreSQL allows you to define your own aggregate functions with CREATE AGGREGATE
This post on the PostgreSQL list shows how trivial it is to create a function to do what's required:
CREATE AGGREGATE textcat_all(
basetype = text,
sfunc = textcat,
stype = text,
initcond = ''
);
SELECT company_id, textcat_all(employee || ', ')
FROM mytable
GROUP BY company_id;
As already mentioned, creating your own aggregate function is the right thing to do. Here is my concatenation aggregate function (you can find details in French):
CREATE OR REPLACE FUNCTION concat2(text, text) RETURNS text AS '
SELECT CASE WHEN $1 IS NULL OR $1 = \'\' THEN $2
WHEN $2 IS NULL OR $2 = \'\' THEN $1
ELSE $1 || \' / \' || $2
END;
'
LANGUAGE SQL;
CREATE AGGREGATE concatenate (
sfunc = concat2,
basetype = text,
stype = text,
initcond = ''
);
And then use it as:
SELECT company_id, concatenate(employee) AS employees FROM ...
This latest announcement list snippet might be of interest if you'll be upgrading to 8.4:
Until 8.4 comes out with a
super-effient native one, you can add
the array_accum() function in the
PostgreSQL documentation for rolling
up any column into an array, which can
then be used by application code, or
combined with array_to_string() to
format it as a list:
http://www.postgresql.org/docs/current/static/xaggr.html
I'd link to the 8.4 development docs but they don't seem to list this feature yet.
Following up on Kev's answer, using the Postgres docs:
First, create an array of the elements, then use the built-in array_to_string function.
CREATE AGGREGATE array_accum (anyelement)
(
sfunc = array_append,
stype = anyarray,
initcond = '{}'
);
select array_to_string(array_accum(name),'|') from table group by id;
Following yet again on the use of a custom aggregate function of string concatenation: you need to remember that the select statement will place rows in any order, so you will need to do a sub select in the from statement with an order by clause, and then an outer select with a group by clause to aggregate the strings, thus:
SELECT custom_aggregate(MY.special_strings)
FROM (SELECT special_strings, grouping_column
FROM a_table
ORDER BY ordering_column) MY
GROUP BY MY.grouping_column
Use STRING_AGG function for PostgreSQL and Google BigQuery SQL:
SELECT company_id, STRING_AGG(employee, ', ')
FROM employees
GROUP BY company_id;
I found this PostgreSQL documentation helpful: http://www.postgresql.org/docs/8.0/interactive/functions-conditional.html.
In my case, I sought plain SQL to concatenate a field with brackets around it, if the field is not empty.
select itemid,
CASE
itemdescription WHEN '' THEN itemname
ELSE itemname || ' (' || itemdescription || ')'
END
from items;
If you are on Amazon Redshift, where string_agg is not supported, try using listagg.
SELECT company_id, listagg(EMPLOYEE, ', ') as employees
FROM EMPLOYEE_table
GROUP BY company_id;
According to version PostgreSQL 9.0 and above you can use the aggregate function called string_agg. Your new SQL should look something like this:
SELECT company_id, string_agg(employee, ', ')
FROM mytable GROUP BY company_id;
You can also use format function. Which can also implicitly take care of type conversion of text, int, etc by itself.
create or replace function concat_return_row_count(tbl_name text, column_name text, value int)
returns integer as $row_count$
declare
total integer;
begin
EXECUTE format('select count(*) from %s WHERE %s = %s', tbl_name, column_name, value) INTO total;
return total;
end;
$row_count$ language plpgsql;
postgres=# select concat_return_row_count('tbl_name','column_name',2); --2 is the value
I'm using Jetbrains Rider and it was a hassle copying the results from above examples to re-execute because it seemed to wrap it all in JSON. This joins them into a single statement that was easier to run
select string_agg('drop table if exists "' || tablename || '" cascade', ';')
from pg_tables where schemaname != $$pg_catalog$$ and tableName like $$rm_%$$

Resources