How to check if json inner field is EMPTY? - postgresql

One of the column ( called details ) in my table is of jsonb data type and have data format somthing like this:
{"media_height":"350", "media_height":"450", "media_alt":"", "file_name":"myfile.jpeg"}
This field I am taking in case when because I want to mark the records of missing alt text.
SELECT
distinct ON ( p.property_name )
p.id, p.property_name,
CASE
WHEN mma.id IS NULL THEN 'Z'
WHEN mma.details->'media_alt'::TEXT IS NULL THEN 'NO'
ELSE 'YES' END as has_media_alt
FROM properties p
LEFT JOIN marketing_media_associations mma ON ( mma.reference_id = p.id )
GROUP BY p.id, p.property_name , mma.details->'media_alt', mma.id
ORDER BY p.property_name, has_media_alt ASC
The above query gives me accurate results for Z, but it never goes in NO block. What I am missing here?

An empty string is not the same as NULL, you probably want:
WHEN nullif(mma.details->>'media_alt', '') IS NULL THEN 'NO'
You don't need to cast to text, if you use ->> which returns the value as text directly.

Related

Get nested value in JSONB column POSTGRESQL

I have a table guest_group with a jsonb column. I want to query one ID where the ID_Context is equal to protelIO.
Here the column of the table:
[
{
"protelSurname":"Smith",
"servicio_tags":[
"protel-info"
],
"protelUniqueID":"[{\"ID\":\"294623726\",\"Type\":\"21\",\"ID_Context\":\"GHA\"},{\"ID\":\"4842148\",\"Type\":\"1\",\"ID_Context\":\"protelIO\"}]",
"protelGivenName":"Seth"
},
{
"value":"test",
"display_name":"Traces",
"servicio_tags":[
"trace"
]
}
]
My try:
SELECT field ->>'protelUniqueID'
FROM guest_group gg
 cross join lateral
jsonb_array_elements(custom_fields) AS field
WHERE value #> '{"servicio_tags": ["protel-info"]}'::jsonb
This gave me:
[{"ID":"294623726","Type":"21","ID_Context":"GHA"},{"ID":"4842148","Type":"1","ID_Context":"protelIO"}]
How can I go the last mile an only get the value of the ID key with the value key pair "ID_Context":"protelIO"?
I appreciate your help!
This will get you the desired result I think. Not very pretty perhaps :-)
select * from (select jsonb_array_elements(f) from (
select (field ->>'protelUniqueID')::jsonb f
FROM guest_group gg,
lateral jsonb_array_elements(custom_fields) AS field
WHERE value #> '{"servicio_tags": ["protel-info"]}'::jsonb
) d(f)) dd(x)
where x->>'ID_Context'='protelIO';

SQL NOT LIKE comparison against dynamic list

Working on a new TSQL Stored Procedure, I am wanting to get all rows where values in a specific column don't start with any of a specific set of 2 character substrings.
The general idea is:
SELECT * FROM table WHERE value NOT LIKE 's1%' AND value NOT LIKE 's2%' AND value NOT LIKE 's3%'.
The catch is that I am trying to make it dynamic so that the specific substrings can be pulled from another table in the database, which can have more values added to it.
While I have never used the IN operator before, I think something along these lines should do what I am looking for, however, I don't think it is possible to use wildcards with IN, so I might not be able to compare just the substrings.
SELECT * FROM table WHERE value NOT IN (SELECT substrings FROM subTable)
To get around that limitation, I am trying to do something like this:
SELECT * FROM table WHERE SUBSTRING(value, 1, 2) NOT IN (SELECT Prefix FROM subTable WHERE Prefix IS NOT NULL)
but I'm not sure this is right, or if it is the most efficient way to do this. My preference is to do this in a Stored Procedure, but if that isn't feasible or efficient I'm also open to building the query dynamically in C#.
Here's an option. Load values you want to filter to a table, left outer join and use PATINDEX().
DECLARE #FilterValues TABLE
(
[FilterValue] NVARCHAR(10)
);
--Table with values we want filter on.
INSERT INTO #FilterValues (
[FilterValue]
)
VALUES ( N's1' )
, ( N's2' )
, ( N's3' );
DECLARE #TestData TABLE
(
[TestValues] NVARCHAR(100)
);
--Load some test data
INSERT INTO #TestData (
[TestValues]
)
VALUES ( N's1 Test Data' )
, ( N's2 Test Data' )
, ( N's3 Test Data' )
, ( N'test data not filtered out' )
, ( N'test data not filtered out 1' );
SELECT a.*
FROM #TestData [a]
LEFT OUTER JOIN #FilterValues [b]
ON PATINDEX([b].[FilterValue] + '%', [a].[TestValues]) > 0
WHERE [b].[FilterValue] IS NULL;

Postgresql order by case when {someCase} then json type column

I need order result from select by few ways.
It's working when it's some column from table TenderItem.
But NOT working if it some key from json type column TenderItem.ItemInfo, f.e.
select * from "TenderItem" order by "ItemInfo" ->> 'Name'; -- working in simple select
with sortingParams (columnName, isAsc) AS (VALUES ('ItemId', true))
select *
FROM "TenderItem" i, sortingParams
WHERE i."TenderId" = 1
AND i."ItemInfo" ->> 'Name' like '%Transcend%'
ORDER BY
case
WHEN columnName like '%ItemId%' THEN i."ItemId" --*work
WHEN columnName like '%ABCSegment%' THEN i."ItemInfo" ->> 'ABCSegment' --**
end desc;
**on this string i have message "ERROR: CASE types bigint and text cannot be matched"
It's not clear how you'd sort the itemID against the ItemInfo segment (unless this points to an item id) since they are not all text values (and if they are all text but some are text strings like '12345' then you do not want to use text sort since then '100' would come before '99'). You probably want them to be separate sort conditions to give more flexibility in ordering:
with sortingParams (columnName, isAsc) AS (VALUES ('ItemId', true))
select *
FROM "TenderItem" i, sortingParams
WHERE i."TenderId" = 1
AND i."ItemInfo" ->> 'Name' like '%Transcend%'
ORDER BY
case
WHEN columnName like '%ItemId%' THEN i."ItemId"::bigint end asc nulls last --puts things with an itemID ahead of those without, or could use nulls first
--if two items have same item id, then sort by segment
, case
WHEN columnName like '%ABCSegment%' THEN i."ItemInfo" ->> 'ABCSegment'
end desc;
Note that each sort condition must give the same datatype for each row being evaluated! This is what gives the error your described where the case statement gives a biting for ItemId and a text value for ItemInfo ->> 'ABCSegment'
ItemId is BIGINT and i."ItemInfo" ->> 'ABCSegment' is text which are incompatible types to do sorting on.
Try casting the value explicitly to BIGINT, i.e
..WHEN columnName like '%ABCSegment%' THEN (i."ItemInfo" ->> 'ABCSegment')::BIGINT
or make i."ItemId" a text if the above fails due to invalid bigint values.
i."ItemId"::TEXT

SQL query to Break on row into multiple row based on some delimiter

I have a table named `test' which has following structure.
category key value
name real_name:Brad,nick_name:Brady,name_type:small NOVALUE
other description cool
But I want to break key column into multiple rows based on , delimiter and value after : delimiter should be a part of value column where value is equal to NOVALUE. So output should look like:
category key value
name real_name Brad
name nick_name Brady
name name_type small
other description cool
How to write sql query for this . I am using postgresql.
Any help ? Thanks in advance.
You can use string_to_array and unnest to do this:
select ts.category,
split_part(key_value, ':', 1) as key,
split_part(key_value, ':', 2) as value
from test ts
cross join lateral unnest(string_to_array(ts.key, ',')) as t (key_value)
where ts.value = 'NOVALUE'
union all
select category,
key,
value
from test
where value <> 'NOVALUE';
SQLFiddle example: http://sqlfiddle.com/#!15/6f1e6/1
select category,
split_part(key_value, ':', 1) as key,
case when value = 'NOVALUE' then split_part(key_value, ':', 2) else value end
from test
cross join lateral unnest(string_to_array(key, ',')) as t (key_value)

Filtering stored procedure records by nested select case statement

I need to further refine my stored proc resultset from this post, I need to filter my resultset to display only records where emailaddr is NULL (meaning display only records that have Invoice_DeliveryType value of 'N' ).
Among numerous queries, I have tried:
select
Invoice_ID, 'Unknown' as Invoice_Status,
case when Invoice_Printed is null then '' else 'Y' end as Invoice_Printed,
case when Invoice_DeliveryDate is null then '' else 'Y' end as Invoice_Delivered,
(case when Invoice_DeliveryType <> 'USPS' then ''
when exists (Select 1
from dbo.Client c
Where c.Client_ID = SUBSTRING(i.Invoice_ID, 1, 6) and
c.emailaddr is not null
)
then 'Y'
else 'N'
end)
Invoice_ContactLName + ', ' + Invoice_ContactFName as ContactName,
from
dbo.Invoice
left outer join
dbo.fnInvoiceCurrentStatus() on Invoice_ID = CUST_InvoiceID
where
CUST_StatusID = 7
AND Invoice_ID = dbo.Client.Client_ID
AND dbo.client.emailaddr is NULL
order by
Inv_Created
but I get an error
The conversion of the nvarchar value '20111028995999' overflowed an int column
How can I get the stored procedure to only return records with DeliveryType = 'N' ?
Trying selecting the stored proc results into a temp table, then select
* from #TempTable
We could really do with a schema definition to get this problem resolved.
It appears that there is an implicit conversion occurring within one of your case statements, but without the schema def's it's difficult to track down which one.
You can't safely mix datatypes in CASE expressions, unless you are absolutely sure that any implicit conversions will work out OK you should make the conversions explicit.
Judging by the error message seeming to include something that could be a date represented as a string(20111028) plus some kind of other data ?time?(995999) it may be something to do with Invoice_DeliveryDate, but this is a shot in the dark without more details.