Sum returns null in Rails - sql

I am trying to subtract two value while querying the data from the database for creating a graph. but it always return null when i try the code
downtime = Ticket.by_month.where(:sla => true).sum("restore_time - start_time")

You have null values in restore_time or start_time. In SQL, null - X and X - null are null for all X as are null + X and X + null. That means that a null value in any of your restore_time or start_time columns will turn the entire sum into null.
You could ignore the rows with nulls:
Ticket.by_month
.where(:sla => true)
.where.not(:restore_time => nil)
.where.not(:start_time => nil)
.sum('restore_time - start_time')
or convert them to something that makes sense in your context:
Ticket.by_month
.where(:sla => true)
.sum('coalesce(restore_time, something_sensible) - coalesce(start_time, something_sensible)')
or perhaps:
Ticket.by_month
.where(:sla => true)
.sum('coalesce(restore_time - start_time, something_sensible)')
Where something_sensible would be what you want to use in place of nulls, this value would of course have to have the same type as the first argument to the coalesce calls.

How about this?
restore_time = Ticket.by_month.where(:sla => true).sum("restore_time")
start_time = Ticket.by_month.where(:sla => true).sum("start_time")
downtime = restore_time - start_time

Related

Linq2DB can't translate a mapped column in Where clause

I'm working with a legacy Oracle database that has a column on a table which stores boolean values as 'Y' or 'N' characters.
I have mapped/converted this column out like so:
MappingSchema.Default.SetConverter<char, bool>(ConvertToBoolean);
MappingSchema.Default.SetConverter<bool, char>(ConvertToChar);
ConvertToBoolean & ConvertToChar are simply functions that map between the types.
Here's the field:
private char hasDog;
[Column("HAS_DOG")]
public bool HasDog
{
get => ConvertToBoolean(hasDog);
set => hasDog = ConvertToChar(value);
}
This has worked well for simply retrieving data, however, it seems the translation of the following:
var humanQuery = (from human in database.Humans
join vetVisit in database.VetVisits on human.Identifier equals vetVisit.Identifier
select new HumanModel(
human.Identifier
human.Name,
human.HasDog,
vetVisit.Date,
vetVisit.Year,
vetVisit.PaymentDue
));
// humanQuery is filtered by year here
var query = from vetVisits in database.VetVisits
select new VetPaymentModel(
(humanQuery).First().Year,
(humanQuery).Where(q => q.HasDog).Sum(q => q.PaymentDue), -- These 2 lines aren't correctly translated to Y/N
(humanQuery).Where(q => !q.HasDog).Sum(q => q.PaymentDue)
);
As pointed out above, the .Where clause here doesn't translate the boolean comparison of HasDog being true/false to the relevant Y/N values, but instead a 0/1 and results in the error
ORA-01722: invalid number
Is there any way to handle this case? I'd like the generated SQL to check that HAS_DOG = 'Y' for instance with the specified Where clause :)
Notes
I'm not using EntityFramework here, the application module that this query exists in doesn't use EF/EFCore
You can define new mapping schema for your particular DataConnection:
var ms = new MappingSchema();
builder = ms.GetFluentMappingBuilder();
builder.Entity<Human>()
.Property(e => e.HasDog)
.HasConversion(v => v ? 'Y' : 'N', p => p == 'Y');
Create this schema ONCE and use when creating DataConnection

Convert SQL Server query to Entity Framework query

I have a SQL Server query like this:
select
month(fact_date) as month,
sum(case when beef_dairy_stat = 1 and param_id = 1 then 1 else 0 end) as cnt
from
user_behave_fact
where
YEAR(fact_date) = 2018
group by
month(fact_date)
order by
month
with a result of
month cnt
------------
1 10
2 20
Now I need to convert this query to its corresponding Entity Framework query.
This is my current attempt:
var sql_rez_ICC = new List<Tuple<int, int>>();
sql_rez_ICC = db.user_behave_fact
.Where(x => x.fact_date.Value.Year == selected_year)
.GroupBy(y => y.fact_date.Value.Month)
.Select(y =>new { month = y.Select(x=>x.fact_date.Value.Month), icc_count = y.Count(x => x.beef_dairy_stat == true && x.param_id == 1) })
.AsEnumerable()
.Select(y => new Tuple<int, int>(y.month, y.icc_count))
.ToList();
However on second .Select, I get an error on month which is
Cannot convert from System.Collection.Generic.IEnumrable to int
y.Select(x=>x.fact_date.Value.Month) returns an IEnumerable<int>. Use y.Key instead.

SQL to Linq group by count

I have a single column in a table to count specific rows. The sql query is as below:
SELECT
CASE
WHEN trail LIKE 'ClassA%' THEN 'ClassA'
WHEN trail LIKE 'ClassB%' THEN 'ClassB'
WHEN trail LIKE 'SemA%' THEN 'SemesterA'
WHEN trail LIKE 'SemB%' THEN 'SemesterB'
END AS Logs
, COUNT(*) AS Count
FROM Logs where s_date >= 'from date from UI' and e_date <= 'to date from ui'
GROUP BY
CASE
WHEN trail LIKE 'ClassA%' THEN 'ClassA'
WHEN trail LIKE 'ClassB%' THEN 'ClassB'
WHEN trail LIKE 'SemA%' THEN 'SemesterA'
WHEN trail LIKE 'SemB%' THEN 'SemesterB'
END
The above query result in sql fine as
ClassA 20
ClassB 5
SemesterA 2
SemesterB 50
Now, I need to change this sql to Linq with a date filter (from date, to date).
Please suggest change in query to simplyfy it.
Thanks
Tried:-
var data = _db.Logs.Where(p => p.trail.StartsWith("ClassA") && (p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)).GroupBy(p => p.trail.StartsWith("ClassA")).Select(s =>
new
{
source = "Class - A total",
percentage = s.Count()
}).Union(_db.Logs.Where(p => p.trail.StartsWith("ClassB") && (p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)).GroupBy(p => p.trail.StartsWith("ClassB")).Select(s =>
new
{
source = "Class - B total",
percentage = s.Count()
}).Union(_db.Logs.Where(p => p.trail.StartsWith("SemesterA") && (p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)).GroupBy(p => p.trail.StartsWith("SemesterA")).Select(s =>
new
{
source = "Semester - A total",
percentage = s.Count()
}).Union(_db.Logs.Where(p => p.trail.StartsWith("SemesterB") && (p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)).GroupBy(p => p.trail.StartsWith("SemesterB")).Select(s =>
new
{
source = "Semester - B total",
percentage = s.Count()
})))).ToList();
Try storing all the interesting starting keys in an enumerable of some sort and then using the built in group by method overload which outputs a result mapped from the key,group pairs (c.f. https://msdn.microsoft.com/en-us/library/vstudio/bb549393(v=vs.100).aspx)
string[] startingKeys = new string[] {"ClassA","ClassB","SemsterA","SemesterB"};
var data =_db.Logs.Where(p=>(p.SDate.Date >= CDate.Date && p.SDate.Date <= FDate.Date)&&startingKeys.Any(k=>p.Logs.StartsWith(k))).GroupBy(p=>startingKeys.Where(k=>p.Logs.StartsWith(k)).First(),(key,items)=>new {source=key,count = items.Count()})
One advantage of this method is you can change the starting keys at runtime if you feel like it.

select distinct out of three column connectiontable

I have a ef connection table with 3 columns in it. I want to select distinct value out of two of them.
I want to select distinct rows orderby ResourceId and MetaDataId.
So i want it to return row (1, 3 and 4) in this case.
Ive tried this:
ctx.ResourceMetas.Where(a => a.ResourceId == resourceid).Distinct()});
But obviously this gets the distinct values out of all three. Can i somehow choose to get distinct out of just the two?
You may group by your distinct values and then get the max or min of the leftovers.
In your case that you only have one more field you could do it like:
ctx.ResourceMetas
.GroupBy(x=>new{x.ResourceId, x.MetaDataId})
.Select
(
x=>new
{
MetaListId = x.Min(m=>m.MetaListId ),
ResourceId = x.Key.ResourceId,
MetaDataId = x.Key.MetaDataId
}
)
.Where(a => a.ResourceId == resourceid)
But in a scenario that you would like the distinct values of 2 fields out of more than three then you would have to do it like:
ctx.ResourceMetas
.GroupBy(x=>new{x.ResourceId, x.MetaDataId})
.Select
(
x=>new
{
MetaListId = x.Where(i=>i.MetaListId == x.Min(m=>m.MetaListId)).FirstOrDefault().MetaListId ,
OtherField = x.Where(i=>i.MetaListId == x.Min(m=>m.MetaListId)).FirstOrDefault().OtherField ,
ResourceId = x.Key.ResourceId,
MetaDataId = x.Key.MetaDataId
}
)
.Where(a => a.ResourceId == resourceid)

How to add whole array to one field in database

I have tried everything, array_push, multidimensional array and so on and nothing worked for me.
Following situation:
try {
if (isset($_SESSION['list']) > 0) {
foreach($_SESSION['list'] as $id=> $quantity) {
$sQuery = "SELECT * FROM table WHERE id = '".$id."' ";
$oStmt = $db->prepare($sQuery);
$oStmt->execute();
while($aRow = $oStmt->fetch(PDO::FETCH_ASSOC)) {
$id = $aRow['id'];
$name = $aRow['name'];
$volume = $aRow['volume'];
}
$testar = array(array('name' => $name, 'volume' => $volume, 'quantity' => $quantity));
$sQuery = "INSERT INTO table (array_data, date) VALUES ('$testar', NOW())";
$oStmt = $db->prepare($sQuery);
$oStmt->execute();
print_r($testar);
}
}
else {
echo 'Nothing to add';
}
}
catch(PDOException $e) {
$sMsg = '<p>
Regelnummer: '.$e->getLine().'<br />
Bestand: '.$e->getFile().'<br />
Foutmelding: '.$e->getMessage().'
</p>';
trigger_error($sMsg);
}
when I print_r($testar); I get this:
Array ( [0] => Array ( [name] => test 1 [volume] => 1.50 [quantity] => 4 ) ) Array ( [0] => Array ( [name] => test 2 [volume] => 2.50 [quantity] => 5 ) ) Array ( [0] => Array ( [name] => test 3 [volume] => 2.50 [quantity] => 2 ) )
but when I add it to database I only see: ARRAY.
How is that possible?
What I want is to add the whole Array to one field in database. Is that possible and how can I arrange that?
Usually, a column in an SQL database should have only one value, not an array. If you have multiple values, you should values individually, either as separate columns if they are a set of totally different types of data, or else as a single column on multiple rows of a dependent table if the array is multiple values of the same type of data.
This rule comes from First Normal Form.
But if you really need to store a PHP array in one row, you can convert the PHP array into a string with PHP's serialize() function. This is better than implode() because serialize() preserves hash keys, arrays of arrays, etc.
$testar = array(array('name' => $name, 'volume' => $volume, 'quantity' => $quantity));
$testar_serialized = serialize($testar);
$sQuery = "INSERT INTO table (array_data, date) VALUES (?, NOW())";
$oStmt = $db->prepare($sQuery);
$oStmt->execute( array($testar_serialized) );
I would start looking at the implode function.
Than you can add all the elements in the array to the field in the database, represented as a string.
A few things:
You don't insert an array into an SQL table; you insert the values of it.
Your SQL table should have a "column" for each field (e.g. name, volume, quantity), and will get a "row" for each entry in the outer array.
You should not have a variable reference in your SQL. This is called an “SQL injection vector,” and (due to accident or malice) will eventually wreak havoc upon you.
What you should do where your INSERT is, is:
prepare your SQL statement once:
INSERT INTO table (name, volume, quantity) VALUES (?,?,?);
The ? mark where the SQL drivers will place your values, which you specify later.
This tells the database to get ready to accept (1 or more) inserts in this form, and
shows the database driver (PDO, mysqli, …) where to put the values when you later
call execute
For each row in the array:
call execute with the list of values, e.g. ->execute ($testar->[$i]->['name'], …