Get Count of Shared DataSet when defined with a Parameter - sql-server

I commonly display the number of rows of my datasets in SSRS e.g.
=CountRows("MyDataSet")
However this doesn't work when the dataset is a shared dataset with a parameter.
=CountRows("MySharedDatasetWithParameter")
Instead it throws an error:
The Value expression for the textrun 'Textbox25.Paragraphs[0].TextRuns[0]' contains an error: (processing): (null != aggregateObj)
How can I get the number of rows in this case?
The dataset "MySharedDatasetWithParameter" does work in normal circumstances, because I am using it to provide the available values to another parameter.
Example of shared dataset with parameter
select [Name], [Value]
from dbo.MyList
where MasterList = #MasterList

A workaround taken from this answer (Its not a duplicate question else I would flag it as such) is to create a hidden, multi-valued, parameter e.g. MyHiddenDataSetValues which stores the values from "MySharedDatasetWithParameter" and then
=Parameters!MyHiddenDataSetValues.Count
gives the number of rows.
Rather clunky, so still hoping for a way to use CountRows.

Related

Crystal Reports Pass database fields as parameter for Stored Procedure

I have a Stored Procedure/SQL function that makes calculations on a specific record returning 0.00 or another DEC value. It takes the record's primary key as a parameter.
I want to implement this function/Stored procedure in Crystal Reports by passing the primary key database field for each individual record. However, when I try to do this, it requires me to manually input the id. The crystal report that I implement is grouped by the id so that there'll be a report for each record.
Is there a way I can pass a database field as a parameter for a Stored Procedure or a SQL function? Is there something similar I can use?
An alternative approach is to insert a subreport using the SP as its data source into the Group Header section. Pass the ID as the subreport link, so it gets used as the SP parameter.
If you need the returned value in main report calculations, use a subreport formula to load the value into a Shared variable so it is accessible to main report formulas.
Another option is to use a Crystal Reports UFL (User Function Library). Ken Hamady maintains a list of UFLs here. At least one of them provides a function that allows you to call a dynamic SQL statement and return a value.
I can't seem to edit a comment to include code, so I'm posting as a new answer to address your new question:
In the subreport, use a formula to declare and set the value of a shared variable. For example:
Shared numbervar CalcResult := {SP_Column_Providing_The_Result};
In the Main Report, in a section BELOW the subreport, simply declare the same shared variable. And use it. It will return the value from the subreport.
If I've understood you correctly, you want to execute some code (a stored procedure or function) which uses the value of a column as the input to the code, and you want to do that for multiple rows at once.
If you write the code as a user defined function (ideally an inline table valued function), which is sort of like a view that takes a parameter, then you can use cross apply or outer apply to execute the code for each row.
Here's a simple example. Suppose I have a block of code written as a user defined function. It takes an integer input parameter and just adds one to the input parameter:
create or alter function MyFunction(#MyParam int) returns table as
return
(
select #MyParam + 1 as MyResult
);
go
Because this function returns a table, I get the result by selecting from it, just like selecting from a regular table or view, like this:
select MyResult from MyFunction(68);
-- returns a result set with a single row and column, with a value of 69
But the input parameter value can also come from values in a column. We provide the value for each row by cross applying the function to the table, because "cross apply" means "run this function for each row". Suppose I have some table with 2 rows in it:
create table MyTable(i int primary key);
go
insert MyTable(i) values (1), (2);
I want to run my function against all of the values of i in the table. I do this using cross apply, and I pass the name of the column as the input parameter:
select f.MyResult
from dbo.MyTable t
cross apply dbo.MyFunction(t.i) f;
The above code returns the following result set:
MyResult
2
3

Assigning value from single row result set in ssis giving error in SSIS 2012

I am trying to get CSV IDs from a table from sql server and assign the result to a variable. below is the sql I have put inside the Execute SQL Task
set nocount on
declare #csv varchar(max) = ''
select #csv = #csv + cast(companyid as varchar(10)) + ',' from company where isprocessed = 0
select substring(#csv,1,len(#csv) - 1) as companyids
As you can see its a simple and standard way of getting csv of a field in t-sql. It works perfectly in query window but throwing below error when I run the Task in SSIS 2012
[Execute SQL Task] Error: The value type (__ComObject) can only be
converted to variables of type Object.
[Execute SQL Task] Error: An error occurred while assigning a value to
variable "sCSVCompanyIds": "The type of the value (DBNull) being
assigned to variable "User::sCSVCompanyIds" differs from the current
variable type (String). Variables may not change type during
execution. Variable types are strict, except for variables of type
Object. ".
Below are the settings of Execute SQL Task
In General tab, Resultset project is set to single row
In Result Set tab, Result Name is set to 0 (I also tried by setting it to csvids which is the alias column name in the select list) and Variable Name is set to User::sCSVCompanyIds
I have no clue why its not working. After wasting so much time I am worked out a hard way which by returning the result as Full Row set (same SQL which returns 1 row 1 column always) and add a for each loop container to loop throw the result set (which will always iterates only once for obvious reasons) and assign the result set's fields to the variable. It works for me but there should be easy way of doing it. What I am missing?
The problem is with the nvarchar(max) data type. I assume it will be same for varchar(max) also. Though relevant data types in SSIS are DT_NTEXT and DT_TEXT are present for some reason we are getting this error.
There are multiple options to handle this.
One Of course there might be more appropriate way to handle this by cast/converting the column within the query to fixed length instead of max something like cast(myvarcharmaxfield as varchar(8000)). In my case it doesn't work because I expect bigger length string. I am generating a csv string from a unique identifier column which itself is 36 chars long string and needs 3 extra chars for quoting them with signle quote and a comma as separator which will support only 205 values. So it doesn't work for me.
So I left with no option but to stick the way I implemented already which is in my question
After wasting so much time I am worked out a hard way which by
returning the result as Full Row set (same SQL which returns 1 row 1
column always) and add a for each loop container to loop throw the
result set (which will always iterates only once for obvious reasons)
and assign the result set's fields to the variable
The new way I learnt is from an unaccepted answer from this question
Change the Oledb connection to ADO.NET connection
I think this is fair enough but it requires me to create another connection manager (so that I don't have to change all existing tasks) and use it for this type of taks where I need csv but I did not buy it as I have very little time to doing research on creating connection string of it which appears to me with the type of erros I am getting is different from oledb.

SSRS error NUMBERORA-00932 expected CHAR got NUMBER and Memory corrupted while trying to display all when parameter is null

I know there are existing questions regarding to this error. but my situation looks different!
I'm creating a SSRS2012 report that can manually input UserIDs number (such as 15, 130, etc..) as the IC_ID. I want to ensure when input is null, report can display all IC_ID. I tried two method to approach it.
Data source: Oracle Sql Developer, table:IC, column: IC_ID datatype:NUMBER
Parameter: UserIDs, datatype: Integer, allow null value.
------------Method 1:---------------
SSRS dataset query, I've tried:
select * from IC
where IC_ID=to_number(COALESCE(:UserIDs, IC_ID))
select * from IC
where IC_ID=to_char(COALESCE(:UserIDs, IC_ID))
select * from IC
where IC_ID=COALESCE(:UserIDs, IC_ID)
But all failed with same error: NUMBERORA-00932 expected CHAR got NUMBER. When I run them in Oracle database, show same error as above. So I guess the problem is how to make data type consistent, column IC_ID data type is NUMBER, if I set parameter UserIDs as Integer, why still not consistent?
PS:when I only use query as:
**select * from IC where IC_ID=(:UserIDs)**
no matter I set parameter datatype to text or integer, the report works fine and display info of whatever IC_ID value I manually input. But it didn't show data when parameter is null!
---------Method 2---------
SSRS dataset query, I've tried:
select * from IC
where IC_ID=(:UserIDs) or (:UserIDs) IS NULL
failed with error: attempted to read or write protected memory. this is often an indication that other memory is corrupt. When I run this query in Oracle, it run well with not error, no matter I pass a number or null to (:UserIDs). So I guess the problem is how to use (:UserIDs) TWICE in SSRS dataset query.
I'm very sure it's not because of memory limit, because this report run well displaying all IC_ID info when there is no parameter at all. It seems it doesn't allow one parameter to show up more than once in dataset query, whenever I tried something like: select * from IC where IC_ID=(:UserIDs) AND IC_SubID=(:UserIDs), it will fail with memory corrupt error.
But it works fine if (:UserIDs) only show up once in query.
---------Method 3----------------
Some posts said in order to display all info when parameter is null, we can add a list of all IC_ID as the default value of parameter. I don't think it works for this case, because my dataset query is actually very complex, starting with a long cte. It will be too complex and slow to add this whole cte query into the list as default value.
I would appreciate if anyone could give some help of how to solve any of above errors, or tell me other ways to display all info while parameter is null!
Thank you
Isn't possible to cast the parameter as an integer? Like cast((:UserIDs as int)) (or maybe cast as a char). Don't know if this helps but i would expect that the datatype is always the same.
Or you could maybe use
(:UserIDs) in (case when (:UserIDs) is null then (select UserIDs from table) else (:UserIDs) end)

SSRS 2012 Lookup Function Returns No Data

I have an SSRS 2012 Report that is based on a MetricsData dataset that contains the field "REPOSITORY". The report also has a second dataset, PlanITData, containing the fields:
RepositoryName
AppName
AppOwnerName
I put an expression into one of the report columns containing the following Lookup:
=Lookup(Fields!REPOSITORY.Value,Fields!RepositoryName.Value,Fields!AppName.Value,"PlanITData")
I expected it would look up the REPOSITORY from MetricsData (the dataset that should be in the scope of the report and does, in fact, display in one of the report columns), find the matching RepositoryName from PlanITData and return the AppName from PlanITData. I get all blanks in this column. I've run the underlying query for the PlanITData dataset with known values and it returns the appropriate values. Do I not understand the Lookup function? This is my first time trying to use it...
Edit: I just saw this warning. I'd swear it wasn't there earlier. No idea what this means:
Warning 1 [rsLookupOfInvalidExpressionDataType] The Value expression
for the textrun ‘LOBGROUP.Paragraphs[0].TextRuns[0]’ uses a lookup
function with an expression that returned a data type that is not
valid for the lookup function. The data type must be an RDL Variant
type.
Apparently the expression above is correct. I kept playing with the datasets and suddenly it started working. Apparently there was an issue with how the datasets were constructed but I'm not sure what it was...

Copy data from lookup column with multiple values to new record Access 2007

I am copying a record from one table to another in Access 2007. I iterate through each field in the current record and copy that value to the new table. It works fine until I get to my lookup column field that allows multiple values. The name of the lookup column is "Favorite Sports" and the user can select multiple values from a dropdown list.
I believe the values of a multivalued field are stored in an array but I cannot access the values in VBA code! I've tried myRecordset.Fields("myFieldName").Value(index) but it didn't work. I don't understand how Access stores multiple values in one field.
I saw something about ItemsSelected on another forum but I don't know what Object is associated with that method.
Thanks for any help!
I would recommend against using multivalue fields for precisely the reason you're running into, because it's extremely complex to refer to the data stored in this simple-to-use UI element (and it's for UI that it's made available, even though it's created in the table design).
From your mention of "ItemsSelected," you seem to be assuming that you access the data in a multivalue field the same way you would in a multiselect listbox on a form. This is not correct. Instead, you have to work with it via a DAO recordset. The documentation for working with multivalue fiels explains how to do it in code, something like this:
Dim rsMyField As DAO.Recordset
Set rsMyField = Me.Recordset("MyField").Value
rsChild.MoveFirst
Do Until rsChild.EOF
Debug.Print rsChild!Value.Value
rsChild.MoveNext
Loop
rsChild.Close
Set rsChild = Nothing
Now, given that you can usually access the properties of a recordset object through its default collections, you'd expect that Me.Recordset("MyField").Value would be returning a recordset object that is navigable through the default collection of a recordset, which is the fields collection. You'd think you could do this:
Me.Recordset("MyField").Value!Value.Value
This should work because the recordset returned is a one-column recordset with the column name "Value" and you'd be asking for the value of that column.
There are two problems with this:
it doesn't actually work. This means that Me.Recordset("MyField").Value is not reallly a full-fledged recordset object the way, say, CurrentDB.OpenRecordset("MyTable") would be. This is demonstrable by trying to return the Recordcount of this recordset:
Me.Recordset("MyField").Value.Recordcount
That causes an error, so that means that what's being returned is not really a standard recordset object.
even if it did work, you'd have no way to navigate the collection of records -- all you'd ever be able to get would be the data from the first selected value in your multivalued field. This is because there is no way in this shortcut one-line form to navigate to a particular record in any recordset that you're referring to in that fashion. A recordset is not like a listbox where you can access both rows and columns, with .ItemData(0).Column(1), which would return the 2nd column of the first row of the listbox.
So, the only way to do this is via navigating the child DAO recordset, as in the code sample above (modelled on that in the cited MSDN article).
Now, you could easily write a wrapper function to deal with this. Something like this seems to work:
Public Function ReturnMVByIndex(ctl As Control, intIndex As Integer) As Variant
Dim rsValues As DAO.Recordset
Dim lngCount As Long
Dim intRecord As Integer
Set rsValues = ctl.Parent.Recordset(ctl.ControlSource).Value
rsValues.MoveLast
lngCount = rsValues.RecordCount
If intIndex > lngCount - 1 Then
MsgBox "The requested index exceeds the number of selected values."
GoTo exitRoutine
End If
rsValues.MoveFirst
Do Until rsValues.EOF
If intRecord = intIndex Then
ReturnMVByIndex = rsValues(0).Value
Exit Do
End If
intRecord = intRecord + 1
rsValues.MoveNext
Loop
exitRoutine:
rsValues.Close
Set rsValues = Nothing
Exit Function
End Function
Using that model, you could also write code to concatenate the values into a list, or return the count of values (so you could call that first in order to avoid the error message when your index exceeded the number of values).
As cool as all of this is, and as nice as the UI that's presented happens to be (it would be really nice if they'd added selection checkboxes as a type for a multiselect listbox), I'd still recommend against using it precisely because it's so much trouble to work with. This just takes the problem of the standard lookup field (see The Evils of Lookup Fields in Tables) and makes things even worse. Requiring DAO code to get values out of these fields is a pretty severe hurdle to overcome with a UI element that is supposed to make things easier for power users, seems to me.
For a quick and dirty way of getting the values out of a multivalued ('complex data') column, you can use an ADO Connection with the Jet OLEDB:Support Complex Data connection property set to False e.g. the connection string should look something like this:
Provider=Microsoft.ACE.OLEDB.12.0;
Data Source=C:\dbs\TestANSI92.accdb;
Jet OLEDB:Engine Type=6;
Jet OLEDB:Support Complex Data=False
The multivaled type column will now be of type MEMO (adLongVarWChar) with each value separated by a semicolon ; character.
But that's only half the problem. How to get data into a multivalued column?
The Access Team seem to have neglected to enhance the Access Database Engine SQL syntax to accommodate multivalued types. The 'semicolon delimiter' trick doesn't work in reverse e.g.
INSERT INTO TestComplexData (ID, weekday_names_multivalued)
VALUES (5, 'Tue;Thu;Sat');
fails with the error, "Cannot perform this operation", ditto when trying to update via ADO recordset :(

Resources