How to add stored procedure variables to SSRS 2012 Labels - ssrs-2012

Very new to SSRS so bear with me...
I created an SSRS report based on a stored procedure and as part of that procedure I calculate a StartDate and EndDate for my report. I need to use those two dates in the Title of the report but those variables are not part of the dataset created from the stored procedure.
How do I add those variables to the Title of the report?
Here is the code:
DECLARE #ThisDate date;
SET #ThisDate = getdate(); -- Current date
DECLARE #sdate AS int
SELECT #sdate = CONVERT(int, CONVERT(varchar(10), dateadd(yy, datediff(yy, 0, #ThisDate) - 2, 0), 112)) -- Beginning of previous 2 year
DECLARE #edate AS int
SELECT #edate = CONVERT(int, CONVERT(varchar(10), dateadd(mm, datediff(mm, -1, #ThisDate) - 1, -1), 112)) -- Last Day of previous month
Want my report to display:
From #sdate through #edate ( from 01-01-2014 through 08-31-2016 )
I appreciate the help!

The way you have it now, I would add a SELECT line to use in a dataset:
SELECT #sdate as START_DATE, #edate AS END_DATE
Your expression would have to use a LOOKUP to get the values from the dataset:
="From " & FIRST(Fields!START_DATE.Value, "DataSet1") & " through " & FIRST(Fields!END_DATE.Value, "DataSet1")
HOWEVER, the better way to do what you want is to use SSRS parameters to calculate your START_DATE and END_DATE and then use the parameters when you need the start and end dates.
START_DATE Parameter -
Date Type: DATE
Available Value Expression:
="01/01/" & (YEAR(TODAY) - 2)

Related

Trying to establish a trigger that counts rows after every update

I have two tables: SKN_ENJIN, and SKN_ENJIN_COUNT
SKN_ENJIN keeps track of usernames and emails.
SKN_ENJIN_COUNT is being used to populate a chart for a dashboard report.
I created this trigger earlier today:
create or replace trigger "BI_SKN_ENJIN_COUNT_TG"
after insert or update or delete on "SKN_ENJIN"
DECLARE
mCount NUMBER;
mDate DATE;
begin
select COUNT(ID) into mCount from SKN_ENJIN where Status = 1;
select TO_DATE(CURRENT_DATE, 'DD-MM-YYYY') into mDate from dual;
MERGE INTO SKN_ENJIN_COUNT c
USING dual d
ON (c.Count_date = mDate)
WHEN MATCHED THEN
UPDATE SET c.Member_count = mCount
WHEN NOT MATCHED THEN
INSERT (Count_date, Member_count)
VALUES (mDate, mCount);
end;
Up until about 10 minutes ago, the trigger worked beautifully. Suddenly, the trigger started throwing ORA-01843: not a valid month
I have tried changing CURRENT_DATE to SYSDATE(), I have tried changing TO_DATE to TO_CHAR. These approaches seemed to cause more errors to appear. What am I missing, and what should I change to solve this problem?
There's no need to call TO_DATE on CURRENT_DATE or SYSDATE. These functions already return DATEs, so there's no need to do any conversion.
In fact, calling TO_DATE on something that is already a DATE forces Oracle to convert it to a string using NLS settings (NLS_DATE_FORMAT) and the convert it back to a date using a given date format picture. If the date format picture you are using does not match NLS_DATE_FORMAT, you will likely end up with errors or incorrect values.
Instead of writing
select TO_DATE(CURRENT_DATE, 'DD-MM-YYYY') into mDate from dual;
you can write
select CURRENT_DATE into mDate from dual;
or just
mDate := CURRENT_DATE;
I don't know what type of the Count_date column in your SKN_ENJIN_COUNT table is, but if it is DATE, it is similarly incorrect to call TO_DATE on it.
I think I found a solution while stumbling my way through it
. It seems that the format of the date is extremely important. Earlier my formatting had been DD-MM-YYYY, when I used MM-DD-YYYY and just a touch of refactoring (ON (TO_DATE(c.Count_date, 'MM-DD-YYYY') = TO_DATE(mDate, 'MM-DD-YYYY')) the script worked without a fuss.
select COUNT(ID) into mCount from SKN_ENJIN where Status = 1;
select sysdate into mDate from dual;
MERGE INTO SKN_ENJIN_COUNT c
USING dual d
ON (TO_DATE(c.Count_date, 'MM-DD-YYYY') = TO_DATE(mDate, 'MM-DD-YYYY'))
WHEN MATCHED THEN
UPDATE SET c.Member_count = mCount

SAS PROC SQL String Manipulation

Can I convert a string column formatted as "YYYY-MM" from SQL back into a Date that uses text. (ie. January, 2014) as a new column using proc SQL?
Initially my source is 2014-01 and I would like every row to be converted to the respective month and year as shown. I have tried the Format option outside of the proc SQL table build, however I need it as a Macro date afterwards.
Thanks
Here's one option that uses a custom format. First convert to date using INPUT() with the ANYDTDTE. format. Then you can either display the variable with the format or you can convert it to a new character variable with the format applied.
/*Create your own format definition*/
proc format;
picture monyyc_fmt (default=25)
low - high = "%B, %Y" (datatype=date);
run;
data want;
str='2014-12';
date=input(str,anydtdte.);
format date monyyc_fmt.;
want=put(date, monyyc_fmt.);
put (_all_) (=/);
run;
proc print data=want;
run;
Results are:
Obs str date want
1 2014-12 December, 2014 December, 2014
References for the custom date format:
http://www2.sas.com/proceedings/sugi31/243-31.pdf
Not sure what PROC SQL has to do with the issue of converting the value. One way would be to convert your string to date using INPUT() and then use the CATX(), PUT() and YEAR() functions and the MONNAME. format to build the new string.
data _null_;
str='2014-12';
date=input(str,anydtdte.);
format date date9.;
want=catx(', ',put(date,monname.),year(date));
put (_all_) (=/);
run;
You could skip the middleman and just parse the string yourself to get the year number. This might be easier to code inside an SQL statement.
want=catx(', ',put(input(str,anydtdte.),monname.),scan(str,1,'-'));
In SQL, consider the next example:
-- The table holding the result
CREATE TABLE #myTable(stringDate CHAR(7), dateValue DATETIME, newStringDate VARCHAR(20))
-- The values in original format
INSERT INTO #myTable(stringDate) SELECT '2014-01'
INSERT INTO #myTable(stringDate) SELECT '2014-02'
INSERT INTO #myTable(stringDate) SELECT '2014-03'
INSERT INTO #myTable(stringDate) SELECT '2015-01'
INSERT INTO #myTable(stringDate) SELECT '2015-02'
GO
-- All months have a first day, so conveting "2014-01" to date is just removing "-" and adding "01"
UPDATE #myTable SET dateValue = CAST(REPLACE(stringDate, '-', '') + '01' AS DATETIME)
GO
-- When we have the dates, you can tranform it to the format you want
UPDATE #myTable SET newStringDate = DATENAME(MONTH, dateValue) + ', ' + CAST(YEAR(dateValue) AS VARCHAR)
GO
SELECT * FROM #myTable

modifying query resultset to include all dates in a range

I have a query which I run on a table TXN_DEC(id, resourceid, usersid, date, eventdesc) which return distinct count of users for a given date-range and resourceid, group by date and eventdesc (each resource can have 4 to 5 eventdesc)
if there is no value of distinct users count on a date in the range, for an eventdesc, then it skips that date row in the resultset.
I need to have all date rows in my resultset or collection such that if there is no value of count for a date,eventdesc combination, then its value is set to 0 but that date still exists in the collection..
How do I go about getting such a collection
I know getting the final dataset entirely from the query result would be too complicated,
but I can use collections in groovy to modify and populate my map/list to get the data in the required format
something similar to following: if
input date range = 5th Feb to 3 March 2011
DataMap = [dateval: '02/05/2011' eventdesc: 'Read' dist_ucnt: 23,
dateval: '02/06/2011' eventdesc: 'Read' dist_ucnt: 23,
dateval: '02/07/2011' eventdesc: 'Read' dist_ucnt: 0, -> this row was not present in query resultset, but row exists in the map with value 0
....and so on till 3 march 2011 and then whole range repeated for each eventdesc
]
If you want all dates (including those with no entries in your TXN_DEC table) for a given range, you could use Oracle to generate your date range and then use an outer join to your existing query. Then you would just need to fill in null values. Something like:
select
d.dateInRange as dateval,
'Read' as eventdesc,
nvl(td.dist_ucnt, 0) as dist_ucnt
from (
select
to_date('02-FEB-2011','dd-mon-yyyy') + rownum - 1 as dateInRange
from all_objects
where rownum <= to_date('03-MAR-2011','dd-mon-yyyy') - to_date('02-FEB-2011','dd-mon-yyyy') + 1
) d
left join (
select
date,
count(distinct usersid) as dist_ucnt
from
txn_dec
where eventDesc = 'Read'
group by date
) td on td.date = d.dateInRange
That's my purely Oracle solution since I'm not a Groovy guy (well, actually, I am a pretty groovy guy...)
EDIT: Here's the same version wrapped in a stored procedure. It should be easy to call if you know the API....
create or replace procedure getDateRange (
p_begin_date IN DATE,
p_end_date IN DATE,
p_event IN txn_dec.eventDesc%TYPE,
p_recordset OUT SYS_REFCURSOR)
AS
BEGIN
OPEN p_recordset FOR
select
d.dateInRange as dateval,
p_event as eventdesc,
nvl(td.dist_ucnt, 0) as dist_ucnt
from (
select
p_begin_date + rownum - 1 as dateInRange
from all_objects
where rownum <= p_end_date - p_begin_date + 1
) d
left join (
select
date,
count(distinct usersid) as dist_ucnt
from
txn_dec
where eventDesc = p_event
group by date
) td on td.date = d.dateInRange;
END getDateRange;

How to handle to_date exceptions in a SELECT statment to ignore those rows?

I have the following query that I am attempting to use as a COMMAND in a crystal report that I am working on.
SELECT * FROM myTable
WHERE to_date(myTable.sdate, 'MM/dd/yyyy') <= {?EndDate}
This works fine, however my only concern is that the date may not always be in the correct format (due to user error). I know that when the to_date function fails it throws an exception.. is it possible to handle this exception in such a way that it ignores the corresponding row in my SELECT statement? Because otherwise my report would break if only one date in the entire database is incorrectly formatted.
I looked to see if Oracle offers an isDate function, but it seems like you are supposed to just handle the exception. Any help would be greatly appreciated. Thanks!!
Echoing Tony's comment, you'd be far better off storing dates in DATE columns rather than forcing a front-end query tool to find and handle these exceptions.
If you're stuck with an incorrect data model, however, the simplest option in earlier versions is to create a function that does the conversion and handles the error,
CREATE OR REPLACE FUNCTION my_to_date( p_date_str IN VARCHAR2,
p_format_mask IN VARCHAR2 )
RETURN DATE
IS
l_date DATE;
BEGIN
l_date := to_date( p_date_str, p_format_mask );
RETURN l_date;
EXCEPTION
WHEN others THEN
RETURN null;
END my_to_date;
Your query would then become
SELECT *
FROM myTable
WHERE my_to_date(myTable.sdate, 'MM/dd/yyyy') <= {?EndDate}
Of course, you'd most likely want a function-based index on the MY_TO_DATE call in order to make this query reasonably efficient.
In 12.2, Oracle has added extensions to the to_date and cast functions to handle conversions that error
SELECT *
FROM myTable
WHERE to_date(myTable.sdate default null on conversion error, 'MM/dd/yyyy') <= {?EndDate}
You could also use the validate_conversion function if you're looking for all the rows that are (or are not) valid dates.
SELECT *
FROM myTable
WHERE validate_conversion( myTable.sdate as date, 'MM/DD/YYYY' ) = 1
If your data is not consistent and dates stored as strings may not be valid then you have 3 options.
Refactor your DB to make sure that the column stores a date datatype
Handle the exception of string to date in a stored procedure
Handle the exception of string to date in a (complex) record selection formula
I would suggest using the first option as your data should be consistent.
The second option will provide some flexibility and speed as the report will only fetch the rows that are needed.
The third option will force the report to fetch every record in the table and then have the report filter down the records.
I have the same problem... an old legacy database with varchar fields for dates and decades of bad data in the field. As much as I'd like to, I can't change the datatypes either. But I came up with this solution to find if a date is current, which seems to be what you're doing as well:
select * from MyTable
where regexp_like(sdate, '[0-1][0-9].[0-3][0-9].[0-9][0-9][0-9][0-9]')
-- make sure it's in the right format and ignore rows that are not
and substr(sdate,7,10) || substr(sdate,1,2) || substr(sdate,4,5) >= to_char({?EndDate}, 'YYYYMMDD')
-- put the date in ISO format and do a string compare
The benefit of this approach is it doesn't choke on dates like "February 30".
Starting from Oracle 12c there is no need to define a function to catch the conversion exception.
Oracle introduced an ON CONVERSION ERROR clause in the TO_DATE function.
Basically the clause suppress the error in converting of an invalid date string (typical errors are ORA-01843, ORA-01841, ORA-011861, ORA-01840) and returns a specified default value or null.
Example of usage
select to_date('2020-99-01','yyyy-mm-dd') from dual;
-- ORA-01843: not a valid month
select to_date('2020-99-01' default null on conversion error,'yyyy-mm-dd') from dual;
-- returns NULL
select to_date('2020-99-01' default '2020-01-01' on conversion error,'yyyy-mm-dd') from dual;
-- 01.01.2020 00:00:00
Solution for the Legacy Application
Let's assume there is a table with a date column stored as VARCHAR2(10)
select * from tab;
DATE_CHAR
----------
2021-01-01
2021-99-01
Using the above feature a VIRTUAL DATE column is defined, that either shows the DATE or NULL in case of the conversion error
alter table tab add (
date_d DATE as (to_date(date_char default null on conversion error,'yyyy-mm-dd')) VIRTUAL
);
select * from tab;
DATE_CHAR DATE_D
---------- -------------------
2021-01-01 01.01.2021 00:00:00
2021-99-01
The VIRTUAL column can be safely used because its format is DATE and if required an INDEX can be set up on it.
select * from tab where date_d = date'2021-01-01';
Since you say that you have "no access" to the database, I am assuming that you can not create any functions to help you with this and that you can only run queries?
If that is the case, then the following code should get you most of what you need with the following caveats:
1) The stored date format that you want to evaluate is 'mm/dd/yyyy'. If this is not the case, then you can alter the code to fit your format.
2) The database does not contain invalid dates such as Feb 30th.
First, I created my test table and test data:
create table test ( x number, sdate varchar2(20));
insert into test values (1, null);
insert into test values (2, '01/01/1999');
insert into test values (3, '1999/01/01');
insert into test values (4, '01-01-1999');
insert into test values (5, '01/01-1999');
insert into test values (6, '01-01/1999');
insert into test values (7, '12/31/1999');
insert into test values (8, '31/12/1999');
commit;
Now, the query:
WITH dates AS (
SELECT x
, sdate
, substr(sdate,1,2) as mm
, substr(sdate,4,2) as dd
, substr(sdate,7,4) as yyyy
FROM test
WHERE ( substr(sdate,1,2) IS NOT NAN -- make sure the first 2 characters are digits
AND to_number(substr(sdate,1,2)) between 1 and 12 -- and are between 0 and 12
AND substr(sdate,3,1) = '/' -- make sure the next character is a '/'
AND substr(sdate,4,2) IS NOT NAN -- make sure the next 2 are digits
AND to_number(substr(sdate,4,2)) between 1 and 31 -- and are between 0 and 31
AND substr(sdate,6,1) = '/' -- make sure the next character is a '/'
AND substr(sdate,7,4) IS NOT NAN -- make sure the next 4 are digits
AND to_number(substr(sdate,7,4)) between 1 and 9999 -- and are between 1 and 9999
)
)
SELECT x, sdate
FROM dates
WHERE to_date(mm||'/'||dd||'/'||yyyy,'mm/dd/yyyy') <= to_date('08/01/1999','mm/dd/yyyy');
And my results:
X SDATE
- ----------
2 01/01/1999
The WITH statement will do most of the validating to make sure that the sdate values are at least in the proper format. I had to break out each time unit month / day / year to do the to_date evaluation because I was still getting an invalid month error when I did a to_date on sdate.
I hope this helps.
Trust this reply clarifies...
there is no direct EXCEPTION HANDLER for invalid date.
One easy way is given below once you know the format like DD/MM/YYYY then below given REGEXP_LIKE function will work like a charm.
to_date() also will work, when invalid_date is found then cursor will goto OTHERS EXCEPTION. given below.
DECLARE
tmpnum NUMBER; -- (1=true; 0 = false)
ov_errmsg LONG;
tmpdate DATE;
lv_date VARCHAR2 (15);
BEGIN
lv_date := '6/2/2018'; -- this will fail in *regexp_like* itself
lv_date := '06/22/2018'; -- this will fail in *to_date* and will be caught in *exception WHEN OTHERS* block
lv_date := '07/03/2018'; -- this will succeed
BEGIN
tmpnum := REGEXP_LIKE (lv_date, '[0-9]{2}/[0-9]{2}/[0-9]{4}');
IF tmpnum = 0
THEN -- (1=true; 0 = false)
ov_errmsg := '1. INVALID DATE FORMAT ';
DBMS_OUTPUT.PUT_LINE (ov_errmsg);
RETURN;
END IF;
tmpdate := TO_DATE (lv_date, 'DD/MM/RRRR');
--tmpdate := TRUNC (NVL (to_date(lv_date,'DD/MM/RRRR'), SYSDATE));
tmpnum := 1;
EXCEPTION
WHEN OTHERS
THEN
BEGIN
tmpnum := 0;
ov_errmsg := '2. INVALID DATE FORMAT ';
DBMS_OUTPUT.PUT_LINE (ov_errmsg || SQLERRM);
RETURN;
END;
-- continue with your other query blocks
END;
-- continue with your other query blocks
DBMS_OUTPUT.PUT_LINE (tmpnum);
END;

SSRS - Oracle DB, Passing Date parameter

Using SSRS with an Oracle Database. I need to prompt the user when running the report to enter a date for report. What is the best way to add in the parameter in my SSRS Report. Having problem finding the right date format. under the "Report Parameter" menu, I have setup the Report Parameters using the DateTime Datatype.
Keep getting this error "ORA-01843: Not a Valid Month"
Thank you for your help.
Select
a.OPR_Name,
a.OPR,
a.Trans_Desc,
a.Trans_Start_Date,
Cast(a.S_Date as date) as S_Date,
Sum(a.Duration) as T
From (
Select
US_F.OPR_Name,
ITH_F.OPR,
ITH_F.ITH_RID,
ITH_F.TRANSACT,
Transact.DESC_1 as Trans_Desc,
To_CHAR(ITH_F.Start_Time,'DD-Mon-YY') as Trans_Start_Date,
To_CHAR(ITH_F.Start_Time,'MM/DD/YYYY') as S_Date,
Substr(To_CHAR(ITH_F.Start_Time,'HH24:MI'),1,6) as Start_Time,
To_CHAR(ITH_F.End_Time,'DD-Mon-YY') as Trans_End_Date,
Substr(To_CHAR(ITH_F.End_Time,'HH24:MI'),1,6) as End_Time,
Cast(Case When To_CHAR(ITH_F.Start_Time,'DD-Mon-YY') = To_CHAR(ITH_F.End_Time,'DD-Mon-YY')
Then (((To_CHAR(ITH_F.End_Time,'SSSSS') - To_CHAR(ITH_F.Start_Time,'SSSSS')) / 60))/60
Else ((86399 - (To_CHAR(ITH_F.Start_Time,'SSSSS')) + To_CHAR(ITH_F.End_Time,'SSSSS'))/60)/60
End as Decimal(3,1)) as Duration
from Elite_76_W1.ITH_F
Left Join Elite_76_W1.Transact
on Transact.Transact = ITH_F.Transact
Left Join Elite_76_W1.US_F
on US_F.OPR = ITH_F.OPR
Where ITH_F.TRANSACT not in ('ASN','QC','LGOT')
) a
Where a.S_Date = #Event_Date
Having Sum(a.Duration) <> 0
Group By a.OPR_Name,
a.OPR,
a.Trans_Desc,
a.Trans_Start_Date,
a.S_Date
Order by a.OPR_Name
Oracle parameters are indicated with a leading colon - #Event_Date should be :Event_Date.
You use CAST(a.S_Date AS DATE) in your query, where a.S_Date is a VARCHAR: To_CHAR(ITH_F.Start_Time, 'MM/DD/YYYY'). If your session date parameter NLS_DATE_FORMAT is different from 'MM/DD/YYYY', this will result in a format error (in your case I suspect your NLS_DATE_FORMAT is something like DD-MON-YYYY, resulting in a "month" error).
A few options:
don't use TO_CHAR in the inner query (always try to keep the date format for internal calculations, use TO_CHAR only where it belongs -- in the GUI). If you only want the date portion, use TRUNC.
use TO_DATE instead of CAST in the outer query: to_date(a.S_Date, 'MM/DD/YYYY'), this is obviously tedious: you cast a date to a varchar that is later transformed to a date.

Resources