I'm new to BIRT and need an answer to the following question:
How to compare two data rows in one data set in BIRT and then print it out to the document?
I am assuming you have a reason for not using a self-join query to bring in the data. One simple thing you could do is have 2 identical datasets and then create a new joint dataset using the 2.
With an Oracle DB, you could easily achieve this with pure SQL using the "Analytic Function" LAG (see the Oracle documentation for details).
Independent from the DB, with BIRT, you could use a variable last_row:
Create some computed columns to keep the results of your comparisons. e.g. "FIRST_COLUMN_CHANGED" as boolean.
afterOpen event:
last_row = null;
onFetch event (pls note I'm not sure wether the actual data columns start at 0 or 1):
if (last_row != null) {
if (last_row[0] == row[0]) {
row["FIRST_COLUMN_CHANGED"] = false;
} else {
row["FIRST_COLUMN_CHANGED"] = true;
}
} else {
// do computations for the first record.
row["FIRST_COLUMN_CHANGED"] = true;
}
// Copy the current row to last_row
last_row = {};
// modify depending on the number of columns
for (var i=0; i<10; i++) {
last_row[i] = row[i];
}
Related
I am having trouble with en EF method returning duplicate rows of data. When I am running this, in my example, it returns four rows from a database view. The fourth row includes details from the third row.
The same query in SSMS returns four individual rows with the correct details. I have read somewhere about EK and problems with optimization when there are no identity column. But - is there anyway to alter the below code to force EK to read all records with all details?
public List<vs_transactions> GetTransactionList(int cID)
{
using (StagingDataEntities db = new StagingDataEntities())
{
var res = from trans in db.vs_transactions
where trans.CreditID == cID
orderby trans.ActionDate descending
select trans;
return res.ToList();
}
}
Found the solution :) MergeOption.NoTracking
public List<vs_transactions> GetTransactionList(int cID)
{
db.vs_transactions.MergeOption = MergeOption.NoTracking;
using (StagingDataEntities db = new StagingDataEntities())
{
var res = from trans in db.vs_transactions
where trans.CreditID == cID
orderby trans.ActionDate descending
select trans;
return res.ToList();
}
}
Need to sort/order a list of data based on an undetermined number of columns (1 or more).
What i'm trying to do is loop through the desired columns and add an OrderBy or ThenBy based on their number to the query'd list, but i'm unsuccessful...
Done this, but it doesn't compile:
var query = GetAllItems(); //returns a IQueriable list of items
//for each selected column
for (int i = 0; i < param.Columns.Length; i++)
{
if (i == 0)
{
query = query.OrderBy(x => x.GetType().GetProperty(param.Columns[i].Name));
}
else
{
//ERROR: IQueriable does not contain a definition for "ThenBy" and no extension method "ThenBy"...
query = query.ThenBy(x => x.GetType().GetProperty(param.Columns[i].Data));
}
}
How can i resolve this issue? Or any alternative to accomplish this requirement?
SOLUTION: #Dave-Kidder's solution is well thought and resolves the compile errors i had. Just one problem, OrderBy only executes (actually sorts the results) after a ToList() cast. This is an issue because i can't convert a ToList back to an IOrderedQueryable.
So, after some research i came across a solution that resolve all my issues.
Microsoft assembly for the .Net 4.0 Dynamic language functionality: https://github.com/kahanu/System.Linq.Dynamic
using System.Linq.Dynamic; //need to install this package
Updated Code:
var query = GetAllItems(); //returns a IQueriable list of items
List<string> orderByColumnList = new List<string>(); //list of columns to sort
for (int i = 0; i < param.Columns.Length; i++)
{
string column = param.Columns[i].Name;
string direction = param.Columns[i].Dir;
//ex.: "columnA ASC"
string orderByColumn = column + " " + direction;
//add column to list
orderByColumnList.Add(orderBy);
}
//convert list to comma delimited string
string orderBy = String.Join(",", orderByColumnList.ToArray());
//sort by all columns, yay! :-D
query.OrderBy(orderBy).ToList();
The problem is that ThenBy is not defined on IQueryable, but on the IOrderedQueryable interface (which is what IQueryable.OrderBy returns). So you need to define a new variable for the IOrderedQueryable in order to do subsequent ThenBy calls. I changed the original code a bit to use System.Data.DataTable (to get a similar structure to your "param" object). The code also assumes that there is at least one column in the DataTable.
// using System.Data.DataTable to provide similar object structure as OP
DataTable param = new DataTable();
IQueryable<DataTable> query = new List<DataTable>().AsQueryable();
// OrderBy returns IOrderedQueryable<TSource>, which is the interface that defines
// "ThenBy" so we need to assign it to a different variable if we wish to make subsequent
// calls to ThenBy
var orderedQuery = query.OrderBy(x => x.GetType().GetProperty(param.Columns[0].ColumnName));
//for each other selected column
for (int i = 1; i < param.Columns.Count; i++)
{
orderedQuery = orderedQuery.ThenBy(x => x.GetType().GetProperty(param.Columns[i].ColumnName));
}
you should write ThenBy after OrderBy like this:
query = query
.OrderBy(t=> // your condition)
.ThenBy(t=> // next condition);
A newbie question. I am using EntityFramework 4.0. The backend database has a function that will return a subset of records based on time.
Example of working code is:
var query = from rx in context.GetRxByDate(tencounter,groupid)
select rx;
var result = context.CreateDetachedCopy(query.ToList());
return result;
I need to verify that a record does not exist in the database before inserting a new record. Before performing the "Any" filter, I would like to populate the context.Rxes with a subset of the larger backend database using the above "GetRxByDate()" function.
I do not know how to populate "Rxes" before performing any further filtering since Rxes is defined as
IQueryable<Rx> Rxes
and does not allow "Rxes =.. ". Here is what I have so far:
using (var context = new EnityFramework())
{
if (!context.Rxes.Any(c => c.Cform == rx.Cform ))
{
// Insert new record
Rx r = new Rx();
r.Trx = realtime;
context.Add(r);
context.SaveChanges();
}
}
I am fully prepared to kick myself since I am sure the answer is simple.
All help is appreciated. Thanks.
Edit:
If I do it this way, "Any" seems to return the opposite results of what is expected:
var g = context.GetRxByDate(tencounter, groupid).ToList();
if( g.Any(c => c.Cform == rx.Cform ) {....}
I am using eclipse e4. I have three timeseries which has inputs dynamically from GUI.
/*some code to calculate the entries from a hashmap*/
for (i = 0; i < statValue.length; i++) {
if(statValue[i].equals("MIN"))
{
timeseries[i] = new TimeSeries(entries.getKey()+statValue[i],Second.class);
}
if(statValue[i].equals("MAX"))
{
timeseries[i] = new TimeSeries(entries.getKey()+statValue[i],Second.class);
}
if(statValue[i].equals("AVG"))
{
timeseries[i] = new TimeSeries(entries.getKey()+statValue[i],Second.class);
}
/* some code to calcluate the input to the timeseries */
if(statValue[i].equals("MIN")){
for(Entry<Timestamp,Long> seriesData : MinutesToMin.entrySet()){
System.out.println(new Second(seriesData.getKey())+" "+seriesData.getValue());
timeseries[i].add(new Second(seriesData.getKey()), seriesData.getValue());
}
dataset.addSeries(timeseries[i]);
}
System.out.println("MAX");
if(statValue[i].equals("MAX")){
for(Entry<Timestamp,Long> seriesData : MinutesToMax.entrySet()){
System.out.println(new Second(seriesData.getKey())+" "+seriesData.getValue());
timeseries[i].add(new Second(seriesData.getKey()), seriesData.getValue());
}
dataset.addSeries(timeseries[i]);
}
System.out.println("AVG");
if(statValue[i].equals("AVG")){
for(Entry<Timestamp,Long> seriesData : MinutesToAvg.entrySet()){
System.out.println(new Second(seriesData.getKey())+" "+seriesData.getValue());
timeseries[i].add(new Second(seriesData.getKey()), seriesData.getValue());
}
dataset.addSeries(timeseries[i]);
}
}
After the series are displayed in Jfreechart. In my code the timeseries may change depending on the "statValue" that I select from my GUI. The timeseries gets dynamically added. I do not want to add the timeseries if it is already present. How to check if timeseries is already present and is it is present I want to remove it.?
Every TimeSeries has a key to identify it. To see if a TimeSeries exists in a TimeSeriesCollection use the getSeries(Comparable) method in that class - it will return null if there is no series with that key. If it returns a non-null value, then you can call the removeSeries(TimeSeries) method to remove it.
I'm using getElementsByTagName to return all the select lists on a page - is it possible to then filter these based upon an option value, ie of the first or second item in the list?
The reason is that for reasons I won't go into here there are a block of select lists with number values (1,2,3,4,5 etc) and others which have text values (Blue and Black, Red and Black etc) and I only want the scripting I have to run on the ones with numerical values. I can't add a class to them which would more easily let me do this however I can be certain that the first option value in the list will be "1".
Therefore is there a way to filter the returned list of selects on the page by only those whose first option value is "1"?
I am pretty sure that there is a better solution, but for the moment you can try something like:
var allSelect = document.getElementsByTagName("select");
var result = filterBy(allSelect, 0/*0 == The first option*/, "1"/* 1 == the value of the first option*/);
function filterBy(allSelect, index, theValue) {
var result = [];
for (var i = 0; i < allSelect.length; i++) {
if(allSelect[i].options[index].value == theValue ) {
result.push(allSelect[i]);
}
}
return result;
}
I managed to get this working by wrapping a simple IF statement around the action to be performed (in this case, disabling options) as follows:
inputs = document.getElementsByTagName('select');
for (i = 0; i < inputs.length; i++) {
if (inputs[i].options[1].text == 1) {
// perform action required
}
}
No doubt there is a slicker or more economic way to do this but the main thing is it works for me.