I'm using postgresql 13 and I'm trying to fetch data from a table based on one of its column.
Said table is defined as follow :
create table my_table (
my_table_id int8 not null,
value varchar(255) not null,
another_table_id int8 not null,
primary key (my_table_id) );
create index my_table__lower_value__idx
ON my_table USING btree (lower((value)::text));
Now, when I'm running both query :
first to select a row with a where clause based on a value defined in another table (column my_table_id)
second to select the same row and the same table based on a value defined in this table (column value).
Second query is not returning any row.
See below :
db > select * from my_table where my_table_id = 1001;
my_table_id | value | another_table_id
------------+--------+-----------------
1 | value1 | 1001
(1 row)
db > select * from my_table where lower(value) = lower('value1');
my_table_id | value | another_table_id
------------+--------+-----------------
(0 rows)
Mind you, if I ran this query with some other values, it works :
db > select * from my_table where my_table_id = 1002;
my_table_id | value | another_table_id
------------+--------+-----------------
2 | value2 | 1002
(1 row)
db > select * from my_table where lower(value) = lower('value2');
my_table_id | value | another_table_id
------------+---------+-----------------
2 | value2 | 1002
(1 row)
Why this difference ?
What I've tried so far :
using select * from my_table where value in (select value from my_table where another_table_id = 1001); does not work
using lower on each part of equal statement: still not working on first case.
using LIKE keyword : it works fine in both cases
Related
I am trying the following with Db2:
Problem
So I've got a table with 80+ columns and two rows.
I need to accomplish is checking what columns have changed value between the two rows, and return a table of the column names that have changed, their initial value from row1, and their new value from row2.
Approach so far
My initial idea was to perform a pivot of the two rows into two columns, row 1 as column 1, row 2 as column 2, then join a column of column names (likely taken from syscat.columns) to the table as column 3, at which point I can then do a select where column1 != column2, hence returning the rows with all the data needed. But alas, it was not long after coming up with this that I discover DB2 doesn't support pivot / unpivot...
Question
So is there any idea for how to accomplish this in DB2, taking a table with 80+ columns and two rows like so:
| Col A | Col B | Col C | ... | Col Z|
| ----- | ----- | ----- | --- | ---- |
| Val A | Val B | 123 | ... | 01/01/2021 |
| Val C | Val B | 124 | ... | 02/01/2021 |
And returning a table with the columns changed, their initial value, and their new value:
| Initial | New | ColName|
| ----- | ----- | ----- |
| Val A | Val C | Col A |
| 123 | 124 | Col C |
| 01/01/2021 | 02/01/2021 | Col Z |
Also note the column data types also vary, so will need to be converted to varchar
DB2 version is 11.1
EDIT: Also for reference as per comment request, this is code I attempted to use to achieve this goal:
WITH
INIT AS (SELECT * FROM TABLE WHERE SOMEDATE=(SELECT MIN(SOMEDATE) FROM TABLE),
LATE AS (SELECT * FROM TABLE WHERE SOMEDATE=(SELECT MAX(SOMEDATE) FROM TABLE),
COLS AS (SELECT COLNAME FROM SYSCAT.COLUMNS WHERE TABNAME='TABLE' ORDER BY COLNO)
SELECT * FROM (
SELECT
COLNAME AS ATTRIBUTE,
(SELECT COLNAME AS INITIAL FROM INIT),
(SELECT COLNAME AS NEW FROM LATE)
FROM
COLS
WHERE
(INITIAL != NEW) OR (INITIAL IS NULL AND NEW IS NOT NULL) OR (INITIAL IS NOT NULL AND NEW IS NULL));
Only issue with this one is that I couldn't figure how to use the values from the COLS table as the columns to be selected
You may easily generate text of the expressions needed, if you don't want to type them manually.
Consider the following example, if you want to print different column values only in 2 rows of the same quite a wide table SYSCAT.TABLES. We use the following query for such an expression generation.
SELECT
'DECODE(I.I, '
|| LISTAGG(COLNO || ', A.' || COLNAME || CASE WHEN TYPENAME NOT LIKE '%CHAR%' AND TYPENAME NOT LIKE '%GRAPHIC' THEN '::VARCHAR(128)' ELSE '' END, ', ')
|| ') AS INITIAL' AS EXPR_INITIAL
, 'DECODE(I.I, '
|| LISTAGG(COLNO || ', B.' || COLNAME || CASE WHEN TYPENAME NOT LIKE '%CHAR%' AND TYPENAME NOT LIKE '%GRAPHIC' THEN '::VARCHAR(128)' ELSE '' END, ', ')
|| ') AS NEW' AS EXPR_NEW
, 'DECODE(I.I, '
|| LISTAGG(COLNO || ', ''' || COLNAME || '''', ', ')
|| ') AS COLNAME' AS EXPR_COLNAME
FROM SYSCAT.COLUMNS C
WHERE TABSCHEMA = 'SYSCAT' AND TABNAME = 'TABLES'
AND TYPENAME NOT LIKE '%LOB';
It doesn't matter how many columns the table contains. We just filter out the columns of *LOB types as an example. If you want them as well, you should change the ::VARCHAR(128) casting to some ::CLOB(XXX).
These 3 generated expressions we put to the corresponding places in the query below:
WITH MYTAB AS
(
-- We enumerate the rows to reference them later
SELECT ROWNUMBER() OVER () RN_, T.*
FROM SYSCAT.TABLES T
WHERE TABSCHEMA = 'SYSCAT'
FETCH FIRST 2 ROWS ONLY
)
SELECT *
FROM
(
SELECT
-- Place here the result got in the EXPR_INITIAL column
-- , Place here the result got in the EXPR_NEW column
-- , Place here the result got in the EXPR_COLNAME column
FROM MYTAB A, MYTAB B
,
(
SELECT COLNO AS I
FROM SYSCAT.COLUMNS
WHERE TABSCHEMA = 'SYSCAT' AND TABNAME = 'TABLES'
AND TYPENAME NOT LIKE '%LOB'
) I
WHERE A.RN_ = 1 AND B.RN_ = 2
)
WHERE INITIAL IS DISTINCT FROM NEW;
The result I got in my database:
|INITIAL |NEW |COLNAME |
|--------------------------|--------------------------|---------------|
|2019-06-04-22.44.14.493001|2019-06-04-22.44.14.502001|ALTER_TIME |
|26 |15 |COLCOUNT |
|2019-06-04-22.44.14.493001|2019-06-04-22.44.14.502001|CREATE_TIME |
|2019-06-04-22.44.14.493001|2019-06-04-22.44.14.502001|INVALIDATE_TIME|
|2019-06-04-22.44.14.493001|2019-06-04-22.44.14.502001|LAST_REGEN_TIME|
|ATTRIBUTES |AUDITPOLICIES |TABNAME |
I have custom types in oracle and then I need to populate the same in Postgres.
This oracle type creation code
CREATE OR REPLACE TYPE vr_return_attendee_rp AS OBJECT (
requestid NUMBER,
startdate VARCHAR2(50),
CONSTRUCTOR FUNCTION vr_return_attendee_rp RETURN SELF AS RESULT
);
/
CREATE OR REPLACE TYPE BODY "VR_RETURN_ATTENDEE_RP" AS
CONSTRUCTOR FUNCTION vr_return_attendee_rp RETURN SELF AS RESULT AS
BEGIN
self.requestid := NULL;
self.visitdate := NULL;
return;
END;
END;
/
CREATE OR REPLACE TYPE "VR_RETURN_ATTENDEE_ARR" AS
VARRAY(10000) OF vr_return_attendee_rp;
And then I created type in Postgres like the following
CREATE TYPE vr_return_attendee_rp AS (
requestid INT,
startdate VARCHAR(50)
);
How to create "VR_RETURN_ATTENDEE_ARR" type in Postgres.
PostgreSQL has native support for array types as well as functions and operators to manipulate them.
create type vr_return_attendee_rp as (request_id int, startdate varchar(50));
CREATE TYPE
create table use_vr_type(id serial primary key, vrdata vr_return_attendee_rp[]);
CREATE TABLE
insert into use_vr_type (vrdata) values (array['(1, "2020-01-01")', '(2, "2020-02-01")']::vr_return_attendee_rp[]);
INSERT 0 1
select * from use_vr_type;
id | vrdata
----+-----------------------------------------------
1 | {"(1,\" 2020-01-01\")","(2,\" 2020-02-01\")"}
(1 row)
select id, vrdata[1].request_id, vrdata[1].startdate from use_vr_type;
id | request_id | startdate
----+------------+-------------
1 | 1 | 2020-01-01
(1 row)
select id, unnest(vrdata) from use_vr_type;
id | unnest
----+-------------------
1 | (1," 2020-01-01")
1 | (2," 2020-02-01")
(2 rows)
select id, a.*
from use_vr_type
cross join lateral unnest(vrdata) with ordinality as a(request_id, startdate, rownum);
id | request_id | startdate | rownum
----+------------+-------------+--------
1 | 1 | 2020-01-01 | 1
1 | 2 | 2020-02-01 | 2
(2 rows)
This question is based on this one. I'm looking for a solution to that question that works in DB2. Here is the original question:
I have the following table
DROP TABLE IF EXISTS `test`.`foo`;
CREATE TABLE `test`.`foo` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Then I try to get records based on the primary key
SELECT * FROM foo f where f.id IN (2, 3, 1);
I then get the following result
+----+--------+
| id | name |
+----+--------+
| 1 | first |
| 2 | second |
| 3 | third |
+----+--------+
3 rows in set (0.00 sec)
As one can see, the result is ordered by id. What I'm trying to achieve is to get the results ordered in the sequence I'm providing in the query. Given this example it should return
+----+--------+
| id | name |
+----+--------+
| 2 | second |
| 3 | third |
| 1 | first |
+----+--------+
3 rows in set (0.00 sec)
You could use a derived table with the IDs you want, and the order you want, and then join the table in, something like...
SELECT ...
FROM mcscb.mcs_premise prem
JOIN mcscb.mcs_serv_deliv_id serv
ON prem.prem_nb = serv.prem_nb
AND prem.tech_col_user_id = serv.tech_col_user_id
AND prem.tech_col_version = serv.tech_col_version
JOIN (
SELECT 1, '9486154876' FROM SYSIBM.SYSDUMMY1 UNION ALL
SELECT 2, '9403149581' FROM SYSIBM.SYSDUMMY1 UNION ALL
SELECT 3, '9465828230' FROM SYSIBM.SYSDUMMY1
) B (ORD, ID)
ON serv.serv_deliv_id = B.ID
WHERE serv.tech_col_user_id = 'CRSSJEFF'
AND serv.tech_col_version = '00'
ORDER BY B.ORD
You can use derived column to do custom ordering.
select
case
when serv.SERV_DELIV_ID = '9486154876' then 1 ELSE
when serv.SERV_DELIV_ID = '9403149581' then 2 ELSE 3
END END as custom_order,
...
...
ORDER BY custom_order
To make the logic a little bit more evident you might modify the solution provided by bhamby like so:
WITH ordered_in_list (ord, id) as (
VALUES (1, '9486154876'), (2, '9403149581'), (3, '9465828230')
)
SELECT ...
FROM mcscb.mcs_premise prem
JOIN mcscb.mcs_serv_deliv_id serv
ON prem.prem_nb = serv.prem_nb
AND prem.tech_col_user_id = serv.tech_col_user_id
AND prem.tech_col_version = serv.tech_col_version
JOIN ordered_in_list il
ON serv.serv_deliv_id = il.ID
WHERE serv.tech_col_user_id = 'CRSSJEFF'
AND serv.tech_col_version = '00'
ORDER BY il.ORD
i have a postgresql table t1 , id integer , data jsonb
id | data
--------------------
1 | {"1":{"11":11},"2":{"12":12}}
and i need a function to extract all key/value in separate rows
like this
key | values
----------------------
1 | {"11":11}
2 | {"12":12}
in "hstore" dataType , there was "hvals" function , do this
but in jsonb i dont find similar function
You are looking for jsonb_each
with t1 (id, data) as (
values (1, '{"1":{"11":11},"2":{"12":12}}'::jsonb)
)
select t.*
from t1, jsonb_each(data) as t(k,v)
returns:
k | v
--+-----------
1 | {"11": 11}
2 | {"12": 12}
I run postgres 9.4, and want to migrate column in my database table to hstore just to be able to make performance comparison.
My current column is key-value pair in jsonb, w/o nested structure.
Any tips how to approach this problem?
Example data:
create table jsons (id int, val jsonb);
insert into jsons values
(1, '{"age":22}'),
(2, '{"height":182}'),
(3, '{"age":30, "height":177}');
Split json objects to key, value pairs:
select id, (jsonb_each_text(val)).key, (jsonb_each_text(val)).value
from jsons
id | key | value
----+--------+-------
1 | age | 22
2 | height | 182
3 | age | 30
3 | height | 177
(4 rows)
Aggregate the pairs and convert them to hstore:
select id, hstore(array_agg(key), array_agg(value))
from (
select id, (jsonb_each_text(val)).key, (jsonb_each_text(val)).value
from jsons
) sub
group by 1
order by 1
id | hstore
----+------------------------------
1 | "age"=>"22"
2 | "height"=>"182"
3 | "age"=>"30", "height"=>"177"
(3 rows)
The same can be accomplished in a more elegant way using lateral join:
select id, hstore(array_agg(key), array_agg(value))
from jsons
cross join jsonb_each_text(val)
group by 1
order by 1;