Column values to a row in Db2 like xargs in Linux - sql

I want to do the following in only SQL:
db2 -x "select colname from syscat.columns where tabschema like 'SYSCAT%' and tabname = 'TABLES' order by colno" | xargs
How can I do that? Convert the list of values into a row, like the xargs in Linux.
I want something dynamic, not with CASE, because I need to change the Tablename and the result should be a row.
Original query:
col1
col2
col3
After xargs
col1 col2 col3
I know there is a function called ARRAY_AGG, but it only works inside a compound block, not a SQL query.
https://www.ibm.com/support/knowledgecenter/en/SSEPGG_10.5.0/com.ibm.db2.luw.sql.ref.doc/doc/r0050494.html

Use the LISTAGG function instead.
db2 -x "select listagg(colname, ' ') within group (order by colno) from syscat.columns where tabschema like 'SYSCAT%' and tabname = 'TABLES'" | xargs

How about this
SELECT TABNAME
, LISTAGG(COLNAME,',') WITHIN GROUP (ORDER BY COLNO) AS COLNAMES
FROM SYSCAT.COLUMNS
WHERE TABSCHEMA LIKE 'SYSCAT%'
GROUP BY TABNAME
ORDER BY TABNAME
it returns output such as this
TABNAME COLNAMES
------------------------------ -----------------------------------
BUFFERPOOLDBPARTITIONS BUFFERPOOLID,DBPARTITIONNUM,NPAGES
BUFFERPOOLEXCEPTIONS BUFFERPOOLID,MEMBER,NPAGES
BUFFERPOOLNODES BUFFERPOOLID,NODENUM,NPAGES

Related

Oracle - Compare column using REGEXP_LIKE

I have a table containing 2 columns: FIRST_PART, SECOND_PART. What I need is to run a query again another table using the FIRST_PART, SECOND_PART as LIKE.
So, something like: SELECT {fields} FROM {table} WHERE {column} LIKE {first_part}%{second_part}
I thought maybe some string I construct and use EXECUTE IMMEDIATE, but there must be another way......
You can use:
SELECT field1,
field2,
field3
FROM table_name t
WHERE EXISTS(
SELECT 1
FROM other_table o
WHERE t.column_name LIKE o.first_part || '%' || o.second_part
);

Filtering out values inside the field using SQL command

I have one table as below:
And I have another table as below:
Now, I want to filter out the image name from table 1-image description field and compare it with image name in the table 2 and return the image ID in the output table. So I want my output table like below:
Could someone please help on this.
You can use substring_index(). Assuming it is the last value:
select substring_index(image_description, 'image=', -1)
from t;
If it can be anywhere in a semicolon separated list:
select substring_index(substring_index(image_description, 'image=', -1), ';', 1)
from t;
Here is a db<>fiddle.
EDIT:
In Oracle, you would simply use regexp_replace():
select regexp_replace(image_description, '^.*image=([^;]+).*', '\1')
from t;
Here is a db<>fiddle for this version.
The rest is simply joining on the values.
It looks like you are using Oracle and not MySql, so try this query with a left join of table1 to table2 and the operator like:
select t1.Name, t2."image id"
from table1 t1 left join table2 t2
on t1."image description" like '%image="' || t2.image || '" />'
This will work for the sample data as you posted it, but if image="..." may appear anywhere inside the string then use:
on t1."image description" like '%image="' || t2.image || '"%'
See the demo.
Results:
> NAME | image id
> :--- | -------:
> x | 1
> b | null
> y | 2
> z | 2
> a | null
Assuming the values for column image_desc has always the same format that ends with the text pattern image=...., the following would work.
SELECT SUBSTR(image_desc, LOCATE('image=', image_desc) + 6) AS image
FROM images;
if your image description content pattern is always the same and the value youa are looking for is at the right od the = char
then you could check for the right part after the = char this way
For mysql
select SUBSTRING_INDEX(your_column_name,'=',-1)
from your_table
where your_column_name like '%image%';
for Oracle you could use
SUBSTR(youc_col, INSTR(you_col, '=') + 1)
select SUBSTR(youc_col, INSTR(you_col, '=') + 1)
from your_table
where your_column_name like '%image%';

How to concatenate a string with a listagg result in redshift?

I'm trying to use LISTAGG on Redshift to aggregate data and then concatenate a string to the LISTAGG result.
tmp_table:
Col1 Col2
1 A
1 B
1 C
2 A
2 B
I would like to do something like this:
CREATE TABLE new_table AS (
SELECT
Col1,
'{"results" : "' || LISTAGG(Col2, ',') WITHIN GROUP (ORDER BY Col1) || '"}' as list_result
FROM
tmp_table
GROUP BY
1
)
With the ideal result looking like this:
new_table:
Col1 Col2
1 {"results" : "A,B,C"}
2 {"results" : "A,B"}
Running this without the concatenated strings works fine, but as soon as I concatenate the string, I get:
ERROR: Column length exceeds maximum allowed (maximum column length is 65535)
I'm sure that this does not exceed the column length, as I've checked the length of the longest LISTAGG result and it's only 30 characters. I've also trying casting the LISTAGG result as a VARCHAR(MAX), with no luck.
I think the problem is that LISTAGG automatically creates a column that is max length of 65535 so if you concatenate something to that it will give you the error.
Does it work if you convert to another string length?
CREATE TABLE new_table AS (
SELECT Col1,
'{"results" : "' || CAST(LISTAGG(Col2, ',') WITHIN GROUP (ORDER BY Col1) as VARCHAR(255)) || '"}' as list_result
FROM tmp_table
GROUP BY 1 ;

How can I concat to one string several values that are returned from a select query

Newby question:
I have a select query that returns one column but several rows. This query looks like 'SELECT COL1 FROM TABLE1'.
The table looks like this:
COL1
-----
Val1
Val2
Val3
So the values that are returned are ‘Val1’, ‘Val2’ and ‘Val3’.
I want to create a query that creates a string that looks like
AA.Val1 BB_Val1, AA.Val2 BB_Val2, AA.Val3 BB_Val3
How do I do that?
Is this what you want?
select listagg('AA.' || col1 || ' BB_' || col1, ', ') within group (order by col1)
from t;

PostgreSQL convert columns to rows? Transpose?

I have a PostgreSQL function (or table) which gives me the following output:
Sl.no username Designation salary etc..
1 A XYZ 10000 ...
2 B RTS 50000 ...
3 C QWE 20000 ...
4 D HGD 34343 ...
Now I want the Output as below:
Sl.no 1 2 3 4 ...
Username A B C D ...
Designation XYZ RTS QWE HGD ...
Salary 10000 50000 20000 34343 ...
How to do this?
SELECT
unnest(array['Sl.no', 'username', 'Designation','salary']) AS "Columns",
unnest(array[Sl.no, username, value3Count,salary]) AS "Values"
FROM view_name
ORDER BY "Columns"
Reference : convertingColumnsToRows
Basing my answer on a table of the form:
CREATE TABLE tbl (
sl_no int
, username text
, designation text
, salary int
);
Each row results in a new column to return. With a dynamic return type like this, it's hardly possible to make this completely dynamic with a single call to the database. Demonstrating solutions with two steps:
Generate query
Execute generated query
Generally, this is limited by the maximum number of columns a table can hold. So not an option for tables with more than 1600 rows (or fewer). Details:
What is the maximum number of columns in a PostgreSQL select query
Postgres 9.4+
Dynamic solution with crosstab()
Use the first one you can. Beats the rest.
SELECT 'SELECT *
FROM crosstab(
$ct$SELECT u.attnum, t.rn, u.val
FROM (SELECT row_number() OVER () AS rn, * FROM '
|| attrelid::regclass || ') t
, unnest(ARRAY[' || string_agg(quote_ident(attname)
|| '::text', ',') || '])
WITH ORDINALITY u(val, attnum)
ORDER BY 1, 2$ct$
) t (attnum bigint, '
|| (SELECT string_agg('r'|| rn ||' text', ', ')
FROM (SELECT row_number() OVER () AS rn FROM tbl) t)
|| ')' AS sql
FROM pg_attribute
WHERE attrelid = 'tbl'::regclass
AND attnum > 0
AND NOT attisdropped
GROUP BY attrelid;
Operating with attnum instead of actual column names. Simpler and faster. Join the result to pg_attribute once more or integrate column names like in the pg 9.3 example.
Generates a query of the form:
SELECT *
FROM crosstab(
$ct$
SELECT u.attnum, t.rn, u.val
FROM (SELECT row_number() OVER () AS rn, * FROM tbl) t
, unnest(ARRAY[sl_no::text,username::text,designation::text,salary::text]) WITH ORDINALITY u(val, attnum)
ORDER BY 1, 2$ct$
) t (attnum bigint, r1 text, r2 text, r3 text, r4 text);
This uses a whole range of advanced features. Just too much to explain.
Simple solution with unnest()
One unnest() can now take multiple arrays to unnest in parallel.
SELECT 'SELECT * FROM unnest(
''{sl_no, username, designation, salary}''::text[]
, ' || string_agg(quote_literal(ARRAY[sl_no::text, username::text, designation::text, salary::text])
|| '::text[]', E'\n, ')
|| E') \n AS t(col,' || string_agg('row' || sl_no, ',') || ')' AS sql
FROM tbl;
Result:
SELECT * FROM unnest(
'{sl_no, username, designation, salary}'::text[]
,'{10,Joe,Music,1234}'::text[]
,'{11,Bob,Movie,2345}'::text[]
,'{12,Dave,Theatre,2356}'::text[])
AS t(col,row1,row2,row3,row4);
db<>fiddle here
Old sqlfiddle
Postgres 9.3 or older
Dynamic solution with crosstab()
Completely dynamic, works for any table. Provide the table name in two places:
SELECT 'SELECT *
FROM crosstab(
''SELECT unnest(''' || quote_literal(array_agg(attname))
|| '''::text[]) AS col
, row_number() OVER ()
, unnest(ARRAY[' || string_agg(quote_ident(attname)
|| '::text', ',') || ']) AS val
FROM ' || attrelid::regclass || '
ORDER BY generate_series(1,' || count(*) || '), 2''
) t (col text, '
|| (SELECT string_agg('r'|| rn ||' text', ',')
FROM (SELECT row_number() OVER () AS rn FROM tbl) t)
|| ')' AS sql
FROM pg_attribute
WHERE attrelid = 'tbl'::regclass
AND attnum > 0
AND NOT attisdropped
GROUP BY attrelid;
Could be wrapped into a function with a single parameter ...
Generates a query of the form:
SELECT *
FROM crosstab(
'SELECT unnest(''{sl_no,username,designation,salary}''::text[]) AS col
, row_number() OVER ()
, unnest(ARRAY[sl_no::text,username::text,designation::text,salary::text]) AS val
FROM tbl
ORDER BY generate_series(1,4), 2'
) t (col text, r1 text,r2 text,r3 text,r4 text);
Produces the desired result:
col r1 r2 r3 r4
-----------------------------------
sl_no 1 2 3 4
username A B C D
designation XYZ RTS QWE HGD
salary 10000 50000 20000 34343
Simple solution with unnest()
SELECT 'SELECT unnest(''{sl_no, username, designation, salary}''::text[] AS col)
, ' || string_agg('unnest('
|| quote_literal(ARRAY[sl_no::text, username::text, designation::text, salary::text])
|| '::text[]) AS row' || sl_no, E'\n , ') AS sql
FROM tbl;
Slow for tables with more than a couple of columns.
Generates a query of the form:
SELECT unnest('{sl_no, username, designation, salary}'::text[]) AS col
, unnest('{10,Joe,Music,1234}'::text[]) AS row1
, unnest('{11,Bob,Movie,2345}'::text[]) AS row2
, unnest('{12,Dave,Theatre,2356}'::text[]) AS row3
, unnest('{4,D,HGD,34343}'::text[]) AS row4
Same result.
If (like me) you were needing this information from a bash script, note there is a simple command-line switch for psql to tell it to output table columns as rows:
psql mydbname -x -A -F= -c "SELECT * FROM foo WHERE id=123"
The -x option is the key to getting psql to output columns as rows.
I have a simpler approach than Erwin pointed above, that worked for me with Postgres (and I think that it should work with all major relational databases whose support SQL standard)
You can use simply UNION instead of crosstab:
SELECT text 'a' AS "text" UNION SELECT 'b';
text
------
a
b
(2 rows)
Of course that depends on the case in which you are going to apply this. Considering that you know beforehand what fields you need, you can take this approach even for querying different tables. I.e.:
SELECT 'My first metric' as name, count(*) as total from first_table UNION
SELECT 'My second metric' as name, count(*) as total from second_table
name | Total
------------------|--------
My first metric | 10
My second metric | 20
(2 rows)
It's a more maintainable approach, IMHO. Look at this page for more information: https://www.postgresql.org/docs/current/typeconv-union-case.html
There is no proper way to do this in plain SQL or PL/pgSQL.
It will be way better to do this in the application, that gets the data from the DB.