Postgres query how to club date and time if time is not null - postgresql

with data as(SELECT c."id",c."accountId",c."name",c."campaignType",c."status",
(CASE WHEN cb."executionDetails"->>'initiatedAt' IS NULL THEN csr."startDate"
ELSE cast(cb."executionDetails"->>'initiatedAt' as TIMESTAMP)
END) as "startDate",
CASE WHEN cb."executionDetails"->>'initiatedAt' IS NOT NULL THEN NULL
ELSE csr."timeSlot"->>'type' END as "timeSlotType",
(CASE WHEN cb."executionDetails"->>'initiatedAt' IS not NULL THEN Null ELSE
-- CASE WHEN csr."timeSlotType"->>'startTime' IS NULL THEN NULL
CASE WHEN csr."timeSlot"->>'type'='MORNING' THEN '07:00'
WHEN csr."timeSlot"->>'type'='AFTERNOON' THEN '12:00'
WHEN csr."timeSlot"->>'type'='EVENING' THEN '17:00'
WHEN csr."timeSlot"->>'type'='CUSTOM' THEN (csr."timeSlot"->>'startTime')::json->>'hour'||':'||((csr."timeSlot"->>'startTime')::json->>'minute')
ELSE csr."timeSlot"->>'startTime' END END )::TIME as "startTime",
split_part(cb."batchRunId", '-',6)::decimal as batchNumber,
'CAMPAIGN' as type
FROM "Campaigns" c
LEFT JOIN "CampaignScheduleRequests" csr
ON c."id"=csr."campaignId"
LEFT JOIN "CampaignBatches" cb
ON csr."id"=cb."requestId")
SELECT * FROM data as d
WHERE d."status" IN ('ACTIVATED')
OUTPUT of the above query
Required o/p
Start time column should be concatenation of start date and startTime

with data as(
SELECT c."id",
c."accountId",
c."name",
c."campaignType",
c."status",
coalesce((cb."executionDetails"->>'initiatedAt')::timestamp,
csr."startDate")
) as "startDate",
CASE WHEN cb."executionDetails" ? 'initiatedAt' THEN NULL
ELSE csr."timeSlot"->>'type'
END as "timeSlotType",
(CASE WHEN cb."executionDetails" ? 'initiatedAt' THEN NULL
ELSE CASE csr."timeSlot"->>'type'
WHEN 'MORNING' THEN '07:00'
WHEN 'AFTERNOON' THEN '12:00'
WHEN 'EVENING' THEN '17:00'
WHEN 'CUSTOM' THEN (csr."timeSlot"->'startTime')->>'hour'
||':'
||(csr."timeSlot"->'startTime')->>'minute'
ELSE csr."timeSlot"->>'startTime' --invalid format could cause problems with ::time
END
END )::TIME as "startTime",
split_part(cb."batchRunId", '-',6)::decimal as batchNumber,
'CAMPAIGN' as type
FROM "Campaigns" c
LEFT JOIN "CampaignScheduleRequests" csr ON c."id"=csr."campaignId"
LEFT JOIN "CampaignBatches" cb ON csr."id"=cb."requestId"
WHERE c."status" IN ('ACTIVATED')
)
SELECT *,
"startDate"+coalesce("startTime",'00:00'::time) as "newStartTimestamp"
FROM data;
Use coalesce() to shorten the null replacements:
CASE WHEN cb."executionDetails"->>'initiatedAt' IS NULL
THEN csr."startDate"
ELSE cast(cb."executionDetails"->>'initiatedAt' as TIMESTAMP)
END
is the same as
coalesce((cb."executionDetails"->>'initiatedAt')::timestamp, csr."startDate")
In CASE you can do a single expression evaluation:
CASE expression
WHEN value1 THEN...
WHEN value2 THEN...
instead of a series of checks
CASE
WHEN expression=value1 THEN...
WHEN expression=value2 THEN...
Instead of casting back to json after using the ->> operator that gives you text: (jsonb->>'key1')::json->>'key2', you can just use -> to keep json output the first time.
? operator lets you check the presence of a key json?'key1' without having to check for null in an attempted read json->>'key1' is null.
You can add time to date or timestamp directly, the same how you'd add an interval. And to avoid nullifying your intitiatedAt-based startDate when adding a null-valued startTime, you can use coalesce() again - which I think was your main question.

Related

case when in where clause postresql

select service_code,service_cat_code,mobile_no,upper(applicant_name_eng) as name,to_char(license_date,'dd/mm/yyyy')as license_from,to_char(license_valid_upto,'dd/mm/yyyy')as license_to,Upper(license_no),district_code,taluk_code,CONCAT(address_building,', ', address_cityvillage,', ',address_locality,', ',address_landmark,', ',address_street) as address
from mst_license
WHERE cast(license_valid_upto as date) = case
WHEN license_valid_upto < now()
THEN
case
when license_valid_upto = '2021-06-30'
then 1 else 0
END
ELSE
case when license_valid_upto > now()
then 1 else 0
End
END
and Upper(license_no)='1SP146924BJP'
I want license valid should be either greater than now or if license valid less than now it must be with the date ''30/06/2021' but when i use above query i get error
ERROR: operator does not exist: date = integer
LINE 3: WHERE cast(license_valid_upto as date) = case
^
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
SQL state: 42883
Character: 418
Help me out guys
The main issue you have is that your case statement returns an integer (1 or 0) but you are trying to compare that to a date, which you cannot do as Postgres is a strict data typing. Even if it did work it would always be false (well except for 1969-12-31/1970-01-01). Moreover the case structure is not needed. The best/correct to compare dates is just use date values. Since you did not indicate the data type for column license_valid_upto so based on how it is used I'll assume it is timestamp with timezone as that is what NOW() returns. Your query becomes:
select service_code
, service_cat_code
, mobile_no
, upper(applicant_name_eng) as name
, to_char(license_date,'dd/mm/yyyy')as license_from
, to_char(license_valid_upto,'dd/mm/yyyy')as license_to
, upper(license_no) as license_no
, district_code
, taluk_code
, concat(address_building,', '
,address_cityvillage,', '
,address_locality,', '
,address_landmark,', '
,address_street) as address
from mst_license
where and upper(license_no)='1SP146924BJP'
and ( cast(license_valid_upto as date) > cast( now() as date)
or (cast (icense_valid_upto as date) < cast( now() as date)
and cast (icense_valid_upto as date) = date '2021-06-30'
)
);
Also, learn for format your queries for readability and so you do not need to scroll right. You, and others looking at your queries later will appreciate it later.

Case statement to ISNULL

I am very new to T-SQL and am looking to simplify this query using isnull.
case
when datediff(d, appdate, disdate) IS NOT NULL THEN datediff(d, appdate, disdate)
ELSE
Case
when appdate is null THEN datediff(d,update,getdate())
when disdate IS NULL THEN datediff(d,appdate,getdate())
END
END
Not much of a simplification but this should do the same thing:
ISNULL(datediff(d, appdate, disdate) ,
CASE WHEN appdate IS NULL THEN datediff(d,update,getdate())
WHEN disdate IS NULL THEN datediff(d,appdate,getdate()) END
)

Select JSON value from a EAV structure result set

Given a result set which is in the EAV structure such as :
id | attributeName | stringValue | intValue | BooleanValue
---------------------------------------------------------------
1 stringFoo v1
1 stringFooList v2
1 stringFooList v3
1 intFoo 10
1 intFooList 10
1 intFooList 20
1 booleanFoo true
1 booleanFooList true
1 booleanFooList true
How can I select all the attributes and value pair as a single value in a JSON/JSONB format , which are something likes:
{
"stringFoo" : "v1" ,
"stringFooList" : ["v2","v3"] ,
"intFoo" : 10 ,
"intFooList" : [10,20],
"booleanFoo" : true,
"booleanFooList" : [true,true]
}
If there are multiple attribute value for an attribute such as stringFooList , it will format it as JSON array.
I am using PostgreSQL 9.6
You can do something like this:
select id, jsonb_object_agg(att, value)
from (
select id,
attributename as att,
case
when count(*) > 1 then
jsonb_agg(coalesce(stringvalue,intvalue::text,booleanvalue::text))
else
to_jsonb(min(coalesce(stringvalue,intvalue::text,booleanvalue::text)))
end as value
from eav
group by id, attributename
) t
group by id;
The inner select aggregates multiple values into an JSON array, single values into JSON scalar values. And the outer query then builds a single JSON value of all rows.
Online example: https://rextester.com/TLCRN79815
#a_horse_with_no_name 's answer gives me a good start. I extend his/her answer and come up the following query such that the elements in the JSON array have the same data type of what defined in PostgreSQL.
select id, jsonb_object_agg(att,
case
when strval is not null then strval
when intvalue is not null then intvalue
else boolVal
end
)
from (
select id,
attributename as att,
case when count(*) > 1 then
jsonb_agg(stringvalue) filter (where stringvalue is not null)
else
to_jsonb(min(stringvalue) filter (where stringvalue is not null))
end as strVal,
case when count(*) > 1 then
jsonb_agg(intvalue) filter (where intvalue is not null)
else
to_jsonb(min(intvalue) filter (where intvalue is not null))
end as intvalue,
case when count(*) > 1 then
jsonb_agg(booleanvalue) filter (where booleanvalue is not null)
else
to_jsonb(bool_and(booleanvalue) filter (where booleanvalue is not null))
end as boolVal
from eav
group by id, attributename
) t
group by id;

Query returning different number of rows, results in crosstab error

I get this error with my query, but I can't figure out what is wrong with it:
Query-specified return tuple has 135 columns but crosstab returns 295.
And here's my query: (The part highlighted in bold returns same number of rows when run separately in pgAdmin.)
SELECT X.*, pi.productcode, pi.productitemdesc, pi.retailsalesprice, cat.productcategorydesc FROM (
SELECT * FROM crosstab (
'SELECT a.productitem AS productitemid, l.locationcode, (CASE WHEN SUM(a.netamount) IS NOT NULL THEN SUM(a.netamount) ELSE 0 END) || ''#'' || (CASE WHEN SUM(a.quantity) IS NOT NULL THEN SUM(a.quantity) ELSE 0 END) AS sales_qty FROM invoiceitem a INNER JOIN invoiceinfo b ON a.invoice = b.invoiceid INNER JOIN locationinfo l ON b.location = l.locationid WHERE b.status !=2 AND l.locationtype = 1 AND l.status = 1 AND TO_CHAR (b.invoicedate, ''YYYY-MM-DD'')>=''2018-03-01'' AND TO_CHAR (b.invoicedate, ''YYYY-MM-DD'')<=''2018-03-03'' GROUP BY a.productitem, l.locationcode ORDER BY a.productitem',
'SELECT l.locationcode FROM locationinfo l INNER JOIN invoiceinfo b ON b.location = l.locationid
WHERE b.status !=2 AND l.locationtype = 1 AND l.status = 1 AND TO_CHAR (b.invoicedate, ''YYYY-MM-DD'')>=''2018-03-01'' AND TO_CHAR (b.invoicedate, ''YYYY-MM-DD'')<=''2018-03-03'' GROUP BY l.locationcode order by l.locationcode')
AS (productitemid int, "0007" text,"BE101" text,"BE1013" text,"BE1014" text,"BE102" text,"BE103" text,"BE1034" text,"BE104" text,"BE1040" text,"BE1043" text,"BE1044" text,"BE1045" text,"BE1046" text,"BE105" text,"BE106" text,"BE107" text,"BE108" text,"BE109" text,"BE110" text,"BE111" text,"BE112" text,"BE123" text,"BE1265" text,"BE1266" text,"BE1271" text,"BE1272" text,"BE1273" text,"BE1274" text,"BE1279" text,"BE1280" text,"BE1281" text,"BE1282" text,"BE1351" text,"BE1400" text,"BE1401" text,"BE1404" text,"BE141" text,"BE142" text,"BE193" text,"BE194" text,"BE2125" text,"BE2126" text,"BE2127" text,"BE2128" text,"BE3001" text,"BE3002" text,"BE3005" text,"BE3006" text,"BE3009" text,"BE3010" text,"BE3031" text,"BE3032" text,"BE3121" text,"BE3122" text,"BE3123" text,"BE3124" text,"BE3127" text,"BE3128" text,"BE3131" text,"BE3132" text,"BE3203" text,"BE3204" text,"BE325" text,"BE3253" text,"BE3254" text,"BE326" text,"BE332" text,"BE3503" text,"BE3504" text,"BE355" text,"BE356" text,"BE365" text,"BE366" text,"BE381" text,"BE382" text,"BE383" text,"BE384" text,"BE400" text,"BE401" text,"BE402" text,"BE403" text,"BE405" text,"BE406" text,"BE408" text,"BE409" text,"BE411" text,"BE412" text,"BE4311" text,"BE4316" text,"BE4401" text,"BE4402" text,"BE4521" text,"BE4522" text,"BE4551" text,"BE4552" text,"BE470" text,"BE473" text,"BE475" text,"BE481" text,"BE482" text,"BE601" text,"BE604" text,"BE609" text,"BE610" text,"BE7040" text,"BE7043" text,"BE7045" text,"BE7046" text,"BE7048" text,"BE7049" text,"BE708" text,"BE7111" text,"BE7112" text,"BE7127" text,"BE7128" text,"BE7217" text,"BE7218" text,"BE7307" text,"BE7308" text,"BE7351" text,"BE7352" text,"BE801" text,"BE802" text,"BE803" text,"BE804" text,"BE831" text,"BE832" text,"BE860" text,"BE861" text,"BE862" text,"BE863" text,"BE865" text,"BE981" text,"BE982" text
)) X
LEFT JOIN productitem pi ON X.productitemid = pi.productitemid
LEFT JOIN productcategory cat ON pi.productcategory = cat.productcategoryid
The bold part of
productitemid int, "0007" text,"BE101" text,"BE1013" text,"BE1014" text,"BE102" text,"BE103" text,"BE1034" text,"BE104" text,"BE1040" text,"BE1043" text,"BE1044" text,"BE1045" text,"BE1046" text,"BE105" text,"BE106" text,"BE107" text,"BE108" text,"BE109" text,"BE110" text,"BE111" text,"BE112" text,"BE123" text,"BE1265" text,"BE1266" text,"BE1271" text,"BE1272" text,"BE1273" text,"BE1274" text,"BE1279" text,"BE1280" text,"BE1281" text,"BE1282" text,"BE1351" text,"BE1400" text,"BE1401" text,"BE1404" text,"BE141" text,"BE142" text,"BE193" text,"BE194" text,"BE2125" text,"BE2126" text,"BE2127" text,"BE2128" text,"BE3001" text,"BE3002" text,"BE3005" text,"BE3006" text,"BE3009" text,"BE3010" text,"BE3031" text,"BE3032" text,"BE3121" text,"BE3122" text,"BE3123" text,"BE3124" text,"BE3127" text,"BE3128" text,"BE3131" text,"BE3132" text,"BE3203" text,"BE3204" text,"BE325" text,"BE3253" text,"BE3254" text,"BE326" text,"BE332" text,"BE3503" text,"BE3504" text,"BE355" text,"BE356" text,"BE365" text,"BE366" text,"BE381" text,"BE382" text,"BE383" text,"BE384" text,"BE400" text,"BE401" text,"BE402" text,"BE403" text,"BE405" text,"BE406" text,"BE408" text,"BE409" text,"BE411" text,"BE412" text,"BE4311" text,"BE4316" text,"BE4401" text,"BE4402" text,"BE4521" text,"BE4522" text,"BE4551" text,"BE4552" text,"BE470" text,"BE473" text,"BE475" text,"BE481" text,"BE482" text,"BE601" text,"BE604" text,"BE609" text,"BE610" text,"BE7040" text,"BE7043" text,"BE7045" text,"BE7046" text,"BE7048" text,"BE7049" text,"BE708" text,"BE7111" text,"BE7112" text,"BE7127" text,"BE7128" text,"BE7217" text,"BE7218" text,"BE7307" text,"BE7308" text,"BE7351" text,"BE7352" text,"BE801" text,"BE802" text,"BE803" text,"BE804" text,"BE831" text,"BE832" text,"BE860" text,"BE861" text,"BE862" text,"BE863" text,"BE865" text,"BE981" text,"BE982" text
AND
SELECT l.locationcode FROM locationinfo l INNER JOIN invoiceinfo b ON b.location = l.locationid
WHERE b.status !=2 AND l.locationtype = 1 AND l.status = 1 AND TO_CHAR (b.invoicedate, ''YYYY-MM-DD'')>=''2018-03-01'' AND TO_CHAR (b.invoicedate, ''YYYY-MM-DD'')<=''2018-03-03'' GROUP BY l.locationcode order by l.locationcode
When run seperately, I get 295 results which is correct. However putting it together in the whole query gets the error.
The manual:
The remaining output columns must have the type of the last column of the source_sql query's result, and there must be exactly as many of them as there are rows in the category_sql query's result.
crosstab(text, text) with a dynamic (!) SELECT query as 2nd parameter is very error prone, since the output column list is static. You should also generate the output column list dynamically. Example:
Dynamic alternative to pivot with CASE and GROUP BY
That aside, the different number of rows returned from the same query, may very well be due to a different timezone setting in the two different sessions.
You have the expression TO_CHAR(b.invoicedate, 'YYYY-MM-DD') in your predicates. If b.invoicedate is type timestamptz the result depends on the timezone setting. Consider:
SET timezone = '+10';
SELECT TO_CHAR(timestamptz '2018-04-07 23:30+0', 'YYYY-MM-DD');
to_char
----------
2018-04-08
SET timezone = '-10';
SELECT TO_CHAR(timestamptz '2018-04-07 23:30+0', 'YYYY-MM-DD');
to_char
----------
2018-04-07
Solution
To remove the dependency on the timezone setting, use absolute values.
to_char() is expensive complication in this place to begin with. Drop that and adjust the predicate.
Provide timestamptz values to match your column. This also allows indexes to be used (if applicable).
Return Table Type from A function in PostgreSQL
Use time zone names (not offsets or abbreviations) to avoid more misunderstandings.
Ignoring timezones altogether in Rails and PostgreSQL
Use dollar-quoting to clean up the quote mess.
Insert text with single quotes in PostgreSQL
I suggest this as 2nd parameter:
$$
SELECT l.locationcode
FROM locationinfo l
JOIN invoiceinfo b ON b.location = l.locationid
WHERE b.status <> 2 AND l.locationtype = 1 AND l.status = 1
AND b.invoicedate >= timestamptz '2018-03-01 Europe/Vienna'
AND b.invoicedate < timestamptz '2018-03-04 Europe/Vienna'
GROUP BY 1
ORDER BY 1
$$
Replace Europe/Vienna in my example with the time zone name defining your dates.
Note 2018-03-04 to include all of 2018-03-03 like you intended.

Case when SQL statement into variable

I need to create a variable for a case when statement on sybase sql. I have this error from my sql client
A SELECT statement that assigns a value to a variable must not be
combined with data-retrieval operations.
What do I need to do to use this variable the right way?
DECLARE #OutputName CHAR(50)
SELECT #OutputName=
(case when (a.M_TRN_TYPE='XSW' or a.M_TRN_TYPE='SWLEG') then 'FXSW'
when (a.M_TRN_TYPE<>'SWLEG' and a.M_TP_DVCS='C') then 'DCS'
when a.M_TRN_TYPE<>'SWLEG' and a.M_TP_DVCS<>'C' and SUBSTRING(c.M_SP_SCHED0,1,2)='+1' then
(case when b.M_DTE_SKIP_1>=a.M_TP_DTEEXP then 'SPOT' else 'OUTR' end)
when a.M_TRN_TYPE<>'SWLEG' and a.M_TP_DVCS<>'C' and SUBSTRING(c.M_SP_SCHED0,1,2)<>'+1' then
(case when b.M_DTE_SKIP_2>=a.M_TP_DTEEXP then 'SPOT' else 'OUTR' end)
end),
case when #OutputName='XSW' and
(case when c.M_DATESKIP='+1OD' then b.M_DTE_SKIP_1 else b.M_DTE_SKIP_2 end)=a.M_TP_DTEFST
then 0
else a.M_NB
end as 'NBI'
from TP_COMPL_PL_REP b
join TP_ALL_REP a on (a.M_NB=b.M_NB and a.M_REF_DATA=b.M_REF_DATA)
left join DM_SPOT_CONTRACT_REP c on (a.M_NB=c.M_NB and a.M_REF_DATA=c.M_REF_DATA)
The problem is that you can't combine a SELECT which sets a variable with a SELECT which returns data on the screen.
You set #OutputName to (case when (a.M_TRN_TYPE='XSW' .. ) but in the same SELECT you're also trying to display the results of:
case
when #OutputName='XSW' and
(case
when c.M_DATESKIP='+1OD'
then b.M_DTE_SKIP_1
else b.M_DTE_SKIP_2
end)=a.M_TP_DTEFST
then 0
else a.M_NB
end as 'NBI'
You can't have these two in the same SELECT, in the format you're using. My recommendation is to split them, one for assigning the variable:
DECLARE #OutputName CHAR(50)
SELECT #OutputName=
(case when (a.M_TRN_TYPE='XSW' or a.M_TRN_TYPE='SWLEG') then 'FXSW'
when (a.M_TRN_TYPE<>'SWLEG' and a.M_TP_DVCS='C') then 'DCS'
when a.M_TRN_TYPE<>'SWLEG' and a.M_TP_DVCS<>'C' and SUBSTRING(c.M_SP_SCHED0,1,2)='+1' then
(case when b.M_DTE_SKIP_1>=a.M_TP_DTEEXP then 'SPOT' else 'OUTR' end)
when a.M_TRN_TYPE<>'SWLEG' and a.M_TP_DVCS<>'C' and SUBSTRING(c.M_SP_SCHED0,1,2)<>'+1' then
(case when b.M_DTE_SKIP_2>=a.M_TP_DTEEXP then 'SPOT' else 'OUTR' end)
end)
from TP_COMPL_PL_REP b
join TP_ALL_REP a on (a.M_NB=b.M_NB and a.M_REF_DATA=b.M_REF_DATA)
left join DM_SPOT_CONTRACT_REP c on (a.M_NB=c.M_NB and a.M_REF_DATA=c.M_REF_DATA)
and one for returning the data:
SELECT case when #OutputName='XSW' and
(case when c.M_DATESKIP='+1OD' then b.M_DTE_SKIP_1 else b.M_DTE_SKIP_2 end)=a.M_TP_DTEFST
then 0
else a.M_NB
end as 'NBI'
from TP_COMPL_PL_REP b
join TP_ALL_REP a on (a.M_NB=b.M_NB and a.M_REF_DATA=b.M_REF_DATA)
left join DM_SPOT_CONTRACT_REP c on (a.M_NB=c.M_NB and a.M_REF_DATA=c.M_REF_DATA)