Cognos - Date variables for many queries - variables

I'm using Bullhorn Canvas which runs on Cognos. I'm looking for the simplest way to have query filters based on a prompt with this dropdown:
THIS WEEK
LAST WEEK
THIS MONTH
LAST MONTH
THIS QUARTER
LAST QUARTER
YEAR TO DATE
The report requires many queries with date filters. As far as I can see, I have to write filters in each query that look like this:
[StartDate] between
(case ?Dates Dropdown?
when 'THISWEEK' then _add_days(current_date, -_day_of_week(current_date, 1) + 1)
when 'LASTWEEK' then _add_days(current_date, -_day_of_week(current_date, 1) - 6)
when 'THISMONTH' then _first_of_month(current_date)
end)
and
(case ?Dates Dropdown?
when 'THISWEEK' then _add_days(current_date, -_day_of_week(current_date,1) + 7)
when 'LASTWEEK' then _add_days(current_date, -_day_of_week(current_date,1))
when 'THISMONTH' then _last_of_month(current_date)
when 'LASTMONTH' then _last_of_month(_add_months(current_date, -1))
end)
This is actually shortened. The real filter will be even bigger than this.
As far as I can see, I have to repeat this monstrous filter in every query, and I'd like to avoid that. In other programming languages, I'd create two variables, for example Date1 and Date2. I'd calculate those variables once, based on the dropdown. Then each query would simply say [StartDate] between [Date1] and [Date2].
Is there a way to do this in Cognos?

As a suggestion, we can use the value prompt with static values to set the ranges and then use a join to have the query (or multiple queries) filter based on that range
Step 1 create a value prompt with static values like this:
Step 2 create a query that will have the data items for the ranges like this:
Each data item has the case statement like you gave above
For example, this would be for the data item FromRange
Step 3 navigate to where you can see all of your queries. From the insertable objects, using a join you will have your query follow the from and to range like this:
The join would look like this (note towards the bottom for the operator we use >= for FROM):
and this for the range (and the <= for the TO):
Remember in the Query from the join you just need to re-add the data items you want to show in your content

Related

Modify Postgres query to use generate_series for overall summation over each of several consecutive range intervals

I'm still quite new with SQL, coming from an ORM-centric environment, so please be patient with me.
Provided with a table in the form of:
CREATE TABLE event (id int, order_dates tsrange, flow int);
INSERT INTO event VALUES
(1,'[2021-09-01 10:55:01,2021-09-04 15:16:01)',50),
(2,'[2021-08-15 20:14:27,2021-08-18 22:19:27)',36),
(3,'[2021-08-03 12:51:47,2021-08-05 11:28:47)',41),
(4,'[2021-08-17 09:14:30,2021-08-20 13:57:30)',29),
(5,'[2021-08-02 20:29:07,2021-08-04 19:19:07)',27),
(6,'[2021-08-26 02:01:13,2021-08-26 08:01:13)',39),
(7,'[2021-08-25 23:03:25,2021-08-27 03:22:25)',10),
(8,'[2021-08-12 23:40:24,2021-08-15 08:32:24)',26),
(9,'[2021-08-24 17:19:59,2021-08-29 00:48:59)',5),
(10,'[2021-09-01 02:01:17,2021-09-02 12:31:17)',48); -- etc
the query below does the following:
(here, 'the range' is 2021-08-03T00:00:00 from to 2021-08-04T00:00:00)
For each event that overlaps with the range
Trim the Lower and Upper timestamp values of order_dates to the bounds of the range
Multiply the remaining duration of each applicable event by the event.flow value
Sum all of the multiplied values for a final single value output
Basically, I get all of the events that overlap the range, but only calculate the total value based on the portion of each event that is within the range.
SELECT SUM("total_value")
FROM
(SELECT (EXTRACT(epoch
FROM (LEAST(UPPER("event"."order_dates"), '2021-08-04T00:00:00'::timestamp) - GREATEST(LOWER("event"."order_dates"), '2021-08-03T00:00:00'::timestamp)))::INTEGER * "event"."flow") AS "total_value"
FROM "event"
WHERE "event"."order_dates" && tsrange('2021-08-03T00:00:00'::timestamp, '2021-08-04T00:00:00'::timestamp, '[)')
GROUP BY "event"."id",
GREATEST(LOWER("event"."order_dates"), '2021-08-03T00:00:00'::timestamp),
LEAST(UPPER("event"."order_dates"), '2021-08-04T00:00:00'::timestamp),
EXTRACT(epoch
FROM (LEAST(UPPER("event"."order_dates"), '2021-08-04T00:00:00'::timestamp) - GREATEST(LOWER("event"."order_dates"), '2021-08-03T00:00:00'::timestamp)))::INTEGER, (EXTRACT(epoch
FROM (LEAST(UPPER("event"."order_dates"), '2021-08-04T00:00:00'::timestamp) - GREATEST(LOWER("event"."order_dates"), '2021-08-03T00:00:00'::timestamp)))::INTEGER * "event"."flow")) subquery
The DB<>Fiddle demonstrating this: https://www.db-fiddle.com/f/jMBtKKRS33Qf2FEoY5EdPA/1
This query started out as a complex set of django annotations and aggregation, and I have simplified it to remove the parts not necessary for this question.
So with the above I get a single total value over the input range (in this case a 1-day range).
But I want to be able to use generate_series to perform this same overall summation to each of several consecutive range intervals
e.g.: query for the total during each of the following ranges:
['2021-08-01T00:00:00', '2021-08-02T00:00:00')
['2021-08-02T00:00:00', '2021-08-03T00:00:00')
['2021-08-03T00:00:00', '2021-08-04T00:00:00')
['2021-08-04T00:00:00', '2021-08-05T00:00:00')
This is somewhat related to my previous question here, but since the timestamps for the queried range are used in so many places within the query, I'm pretty lost for how to do this.
Any help/direction will be appreciated.
This should get you started: https://www.db-fiddle.com/f/qm4F7qqWZMrtXtMejimVJr/1.
Basically what I did was to prepare the ranges with a CTE up-front, then select from that table expression with a CROSS JOIN LATERAL of your original query. Next, I replaced all occurrences of 20210803 with lower(target_range) and 20210804 with upper(target_range), then added the GROUP BY of target_range. Note that only those ranges that overlap at least one row in the input will appear in the output; change the cross join to a LEFT JOIN to always see your input ranges in the output, even if value is null. (If so, ON TRUE is fine for the join condition, since you already do the filtering the WHERE of the inner subquery.)

SQL ignore all blank fields in calculation

I am using the following query in SQL within PhpRunner:
SELECT
[Date],
(MondayStrengthEnd1Sets*MondayStrengthEnd1Reps*MondayStrengthEnd1Distance) + (MondayStrengthEnd2Sets*MondayStrengthEnd2Reps*MondayStrengthEnd2Distance) AS Total
FROM Running
When I run the query I get a blank for an answer. Some of the fields will not necessarily be filled in for every record. The example above is just a snippet of all the fields that is in the table and in the complete calculation, there will be almost 90 fields in total. All the fields are from the same table.
What can I add to the query to treat the blank fields as blanks and not as zeros in order to still calculate the total despite some fields not being filled in? If there is anything that will do it automatically for all the fields it would be great.
I am aiming for something like this:
It looks like you need something like:
SELECT
[Date],
isnull(MondayStrengthEnd1Sets*MondayStrengthEnd1Reps*MondayStrengthEnd1Distance, 0)
+ isnull(MondayStrengthEnd2Sets*MondayStrengthEnd2Reps*MondayStrengthEnd2Distance, 0)
AS Total
FROM Running

How to use aggregate function to filter a dataset in ssrs 2008

I have a matrix in ssrs2008 like below:
GroupName Zone CompletedVolume
Cancer 1 7
Tunnel 1 10
Surgery 1 64
ComplatedVolume value is coming by a specific expression <<expr>>, which is equal to: [Max(CVolume)]
This matrix is filled by a stored procedure that I am not supposed to change if possible. What I need to do is that not to show the data whose CompletedVolume is <= 50. I tried to go to tablix properties and add a filter like [Max(Q9Volume)] >= 50, but when I try to run the report it says that aggregate functions cannot be used in dataset filters or data region filters. How can I fix this as easy as possible?
Note that adding a where clause in sql query would not solve this issue since there are many other tables use the same SP and they need the data where CompletedVolume <= 50. Any help would be appreciated.
EDIT: I am trying to have the max(Q9Volume) value on SP, but something happening I have never seen before. The query is like:
Select r.* from (select * from results1 union select * from results2) r
left outer join procedures p on r.pid = p.id
The interesting this is there are some columns I see that does not included by neither results1/results2 nor procedures tables when I run the query. For example, there is no column like Q9Volume in the tables (result1, result2 and procedures), however when I run the query I see the columns on the output! How is that possible?
You can set the Row hidden property to True when [Max(CVolume)] is less or equal than 50.
Select the row and go to Row Visibility
Select Show or Hide based on an expression option and use this expression:
=IIF(
Max(Fields!Q9Volume.Value)<=50,
True,False
)
It will show something like this:
Note maximum value for Cancer and Tunnel are 7 and 10 respectively, so
they will be hidden if you apply the above expression.
Let me know if this helps.

Using IF THEN in Access 2010 Query

I'm not very knowledgeable in coding of Access queries, so I hope someone can help with this issue.
I have a query (using the query builder) that has a field named RetrainInterval from table tblProcedures (this will return a number like 1, 3, 6, 12, etc.; the rotational months the particular document have to be retrained on) and another field named Training/Qualification Date from table tblTrainingRecords.
I want the query to look at the RetrainInterval for a given record (record field is ClassID in tblProcedures) and then look at the Training/Qualification Date and calculate if that record should be in the query.
In a module I would do this:
IF RetrainInterval = 1 Then
DateAdd("m",1,[Training/Qualification Date]) <add to query if <=today()+30>
ElseIf RetrainInterval = 3 Then
DateAdd("m",3,[Training/Qualification Date]) <add to query if <=today()+30>
ElseIF......
How can I translate this into something that would work in a query? My end goal is to generate a report that will show me what document class numbers are due within a specified time interval (say I enter 30 in the form textbox to represent any upcoming required training within 30 days of the query), but all of the calculations to determine this is based off of when the last training date was (stored in the training records table). I also want to make sure that I do not get multiple returns for the same class number since there will be multiple training entries for each class, just grab the minimum last training date. I hope I explained it well enough. It's hard to put this into words on what I am trying to do without positing up the entire database.
UPDATE
I think I have simplified this a bit after getting some rest. Here are two images, one is the current query, and one is what comes up in the report. I have been able to refine this a bit, but now my problem is I only want the particular Class to show once on the report, not twice, even though I have multiple retrain due dates (because everything is looking at the table that holds the employee training data and will have multiple training's for each Class number). I would like to only show one date, the oldest. Hope that makes sense.
Query - http://postimg.org/image/cpcn998zx/
Report - http://postimg.org/image/krl5945l9/
When RetrainInterval = 1, you add 1 month to [Training/Qualification Date].
When RetrainInterval = 3, you add 3 months to [Training/Qualification Date].
And so on.
The pattern appears to be that RetrainInterval is the number of months to add. If that is true, use RetrainInterval directly in your DateAdd() expression and don't bother about IF THEN.
DateAdd("m", RetrainInterval, [Training/Qualification Date])
You can not do that in a query. Been there, cursed that!
You can use the IFF( 2>x ; 1 ;0)
Giving that if the first statement is true, 1 is returned, and 0 if false.
You can not return a criteria like IFF(2>x ; Cell>2 ; Cell>0) (Not possible) It will just return 0 if you try, i think. it will not give an error all the time.
You have to use criterias!
I would to something like this picture:
I hope you follow, else let me know.

SQL query to produce a time x day grid from a list of timestamps?

Structures of my tables are as follow.
Table Name : timetable
timetable http://www.4shared.com/download/MYafV7-6ce/timetableTable.png
Table Name : slot_table
timetable http://www.4shared.com/download/9Lp_CBn2ba/slot_table.png
Table Name : instructor(this table is not required for this particular problem)
I want to show the resultant data in my android app in a timetable format somewhat like this:
random http://www.4shared.com/download/oAGiUXVAba/random.png
Question : What query i should write so that subjects of particular days with respective slots will be the result of the query?
1)The days should be in order like monday,tuesday,wednesday.
2)If monday has 2 subjects in 2 different slots then it should display like this :
Day 7:30-9:10AM 9:20-11:00AM
Monday Android Workshop Operating System
This is just a sample.
P.S:As timetable format is required,all the subjects with slot ids of all the days(monday to saturday) must be there in it.
Edit :
I tried this
select day,subject,slot from timetable,slot_table where timetable.slotid = slot_table.slotid
which gave a result :
a http://www.4shared.com/download/uMU7NA8Oce/random1.png
But i want it in a timetable format which i am not having an idea how to do that.
Edit :
Timetable sample format is something like this :
Edit :
I wrote a query
select timetable.day,count(slot_table.subject) as no_of_classes from timetable,slot_table where timetable.slotid = slot_table.slotid group by timetable.day
which resulted in
a http://www.4shared.com/download/rZW20_g8ce/random2.png
So now it shows monday has 2 classes in 2 slots,Tuesday has 1 class in 1 slot and so on.
Now any help on a query which can show the two slots(timings) on monday?
Solution :
select timetable.day,max(case when (slot='7:30-9:10AM') then slot_table.subject END) as "7:30-9:10AM",max(case when (slot='9:20-11:00AM') then slot_table.subject END) as "9:20-11:00AM",max(case when (slot='11:10-12:50PM') then slot_table.subject END) as "11:10-12:50PM",max(case when (slot='1:40-3:20PM') then slot_table.subject END) as "1:40-3:20PM", max(case when (slot='3:30-5:00PM') then slot_table.subject END) as "3:30-5:00PM" from timetable join slot_table on timetable.slotid = slot_table.slotid group by timetable.day
Result :
a http://www.4shared.com/download/1w7Tyicfce/random3.png
What you want is called a PIVOT query. In one of these, you have a select which gives the data in rows, like your result just under the EDIT (Day, subject, slot). Then you need to specify the values of the row you want to 'pivot' to become columns (slot in this example). Because a Pivot relies on the values of the column to be pivoted it can be difficult to write a general query, and the Postgres Wiki has an example using dymanic SQL and lots of code generating it at http://wiki.postgresql.org/wiki/Pivot_query
In your case, given that slots look like they're fixed and you might be able to hard-code them (that's a decision you'll have make yourself).
NB I am not a Postgres user, but it looks like it can do it (and I would have been very surprised if it couldn't).
This is a pivot or crosstab query. PostgreSQL has only limited support for these via the crosstab function in the tablefunc module.
It can sometimes be better to just deal with this in the application, accumulating the data into a table as you read each data point.