How to compare and sum values of multiple periods using LINQ in VB.Net - vb.net

I have the following example datatable:
Value1 Value2 Customer Product Date
100 50 1000 100 1.8.2010
50 20 1000 101 5.1.2010
200 60 1000 100 6.2.2011
180 100 1001 100 7.3.2010
500 700 1000 100 1.1.2010
300 300 1001 100 4.4.2011
250 600 1000 100 3.3.2011
And now the user should be able to compare multiple periods. In this example the user chose two periods: 1.1.2010 - 31.12.2010 and 1.1.2011 - 31.12.2011. The result of the example should be:
Customer Product SumValue1Period1 SumValue2Period1 SumValue1Period2 SumValue2Period2
1000 100 600 750 450 660
1000 101 50 20 0 0
1001 100 300 100 300 300
How can I do this?

Since you have known number of columns, you can group data by Customer and products and then take conditional sum from grouping and it will make different columns of the resultant query.
Please have a look at following LinqPad program. Sorry, I'm not familiar with VB.Net so I have coded it in C#, but you'll get the fair idea:
void Main()
{
var Period1Start = new DateTime(2010,1,1);
var Period1End = new DateTime(2010,12,31);
var Period2Start = new DateTime(2011,1,1);
var Period2End = new DateTime(2011,12,31);
List<Item> lst = new List<Item>
{
new Item{ Value1 = 100, Value2 = 50, Customer = 1000, Product = 100 , Date = new DateTime(2010,8,1)},
new Item{ Value1 = 50, Value2 = 20, Customer = 1000, Product = 101 , Date = new DateTime(2010,5,1)},
new Item{ Value1 = 200, Value2 = 60, Customer = 1000, Product = 100 , Date = new DateTime(2011,2,6)},
new Item{ Value1 = 180, Value2 = 100, Customer = 1001, Product = 100 , Date = new DateTime(2010,7,3)},
new Item{ Value1 = 500, Value2 = 700, Customer = 1000, Product = 100 , Date = new DateTime(2010,1,1)},
new Item{ Value1 = 300, Value2 = 300, Customer = 1001, Product = 100 , Date = new DateTime(2011,4,4)},
new Item{ Value1 = 250, Value2 = 600, Customer = 1000, Product = 100 , Date = new DateTime(2011,3,3)}
};
var grp = lst.GroupBy(x=>new{x.Customer, x.Product}).
Select(y=> new
{
Customer = y.Key.Customer,
Product = y.Key.Product,
SumValue1Period1 = y.Where(x=>x.Date >= Period1Start && x.Date<= Period1End).Sum(p=>p.Value1),
SumValue2Period1 = y.Where(x=>x.Date >= Period1Start && x.Date<= Period1End).Sum(p=>p.Value2),
SumValue1Period2 = y.Where(x=>x.Date >= Period2Start && x.Date<= Period2End).Sum(p=>p.Value1),
SumValue2Period2 = y.Where(x=>x.Date >= Period2Start && x.Date<= Period2End).Sum(p=>p.Value2)
});
Console.WriteLine(grp);
}
// Define other methods and classes here
public class Item
{
public int Value1{get;set;}
public int Value2{get;set;}
public int Customer{get;set;}
public int Product{get;set;}
public DateTime Date{get;set;}
}

Take a look at
http://msdn.microsoft.com/en-us/vbasic/bb737908
Specifically, the 'GroupBy - Nested' example. It shows using LINQ to group by 'customer's orders, first by year, and then by month.' You situation should be more straight forward since it's just the date range.

Related

Pandas: Product price calculation by carrier

I have a tariff 'tarifas' of the Sudoeste and Termaco carriers
dataframe and a products 'dpx' dataframe
tarifas = {
'Sudoeste': {
'PR': {
'Capital': {'frete_minimo': 47.40,'rate': 0.35,'pedagio_frac': 2.83,'gris': 0.0012,'taxa': 3.36},
'Interior': {'frete_minimo': 48.34,'rate': 0.36,'pedagio_frac': 2.83,'gris': 0.0012,'taxa': 3.36},},}
'Termaco': {
'CE':{
'Capital': {'frete_minimo': 52.69,'Exce_50': 1.06,'pedagio_frac': 2.64,'gris': 0.0011,'taxa': 42.71},
'Interior': {'frete_minimo': 68.76,'Exce_50': 1.39,'pedagio_frac': 2.64,'gris': 0.0011,'taxa': 42.71},},}
I have a function to calculate the delivery freight of a product to a certain region, but it is giving an error.
def calcular_frete(dpx, tarifas):
for index, row in dpx.iterrows():
uf = row['Estado do Destinatário']
regiao = row['regiao']
peso_taxado = row['Peso Taxado']
valor_mercadoria = row['Valor da Mercadoria']
for transportadora, tarifas_transp in tarifas.items():
tarifas = tarifas_transp[uf][regiao]
frete_minimo = tarifas['frete_minimo']
exce_50 = tarifas.get('Exce_50', 0)
pedagio_frac = tarifas['pedagio_frac']
gris = tarifas['gris']
taxa = tarifas['taxa']
rate = tarifas['rate']
if transportadora =='Sudoeste':
if peso_taxado > 50:
valor_frete = frete_minimo + (peso_taxado - 50) * rate + (peso_taxado / 100) * pedagio_frac + (valor_mercadoria * gris) + (valor_mercadoria * taxa)
else:
valor_frete = frete_minimo + (peso_taxado / 100) * pedagio_frac + (valor_mercadoria * gris) + (valor_mercadoria * taxa)
When I call the function to calculate shipping calcular_frete(dpx, tarifas), it results in KeyError: 'SC' in 'tarifas = tarifas_transp[uf][regiao]'
What can I do?

How can I achieve SQL Pivot statement from LINQ

I am looking to achieve below SQL statement from LINQ. I am not sure whether is it possible? Can someone advice me on this?
SELECT *
FROM (
SELECT CONVERT(VARCHAR, (DATEADD(WEEK, DATEDIFF(WEEK, 0, S.SampleDrawn), 0)), 101) [Date], [Range] =
CASE
WHEN ProbBacteremia >= 0 AND ProbBacteremia < 0.50 THEN 'Low'
WHEN ProbBacteremia >= 0.50 AND ProbBacteremia < 0.75 THEN 'Med'
ELSE 'High'
END
FROM Result.Calculation C INNER JOIN Data.SampleSet S ON C.SampleSetID = S.ID WHERE S.SampleDrawn >= DATEADD(WEEK,-1,GETDATE())) o
PIVOT
(
COUNT(o.[Range])
FOR [Range] IN (
[Low], [Med], [High])
) pt
ORDER BY [Date]
Result of the above query will be as below
Date Low Med High
09/04/2017 370 174 175
09/11/2017 764 352 389
09/18/2017 759 384 360
09/25/2017 765 385 404
10/02/2017 115 48 56
Note that, above date has grouped by week. Ie. 09/04 , 09/11, 09/18 etc. I did lot of research but i found only to group by Week Number.
This is as far as i could come up with LINQ which will return me the below result set.
data = (from a in context.Calculations
where a.SampleSet.SampleDrawn >= dtStart && (isDeptFilter || a.SampleSet.Department == location)
group a by new { Text = RangeProvider(a.ProbBacteremia * 100, riskCats), Date = a.SampleSet.SampleDrawn.Date } into groupedData
orderby groupedData.Key.Date ascending
select new { Value = groupedData.Count(), Text = groupedData.Key.Text, Date = groupedData.Key.Date.ToShortDateString() }).ToList();
public static string RangeProvider(int value)
{
if (value > 0 && value <= 25)
{ return "Low"; }
if (value > 25 && value <= 75)
{ return "Medium"; }
if (value > 75 && value <= 90)
{ return "High"; }
else
{ return "Very High"; }
}
Result dataset of the obver LINQ is
Date Text Value
09/04/2017 Low 65
09/04/2017 Med 80
09/04/2017 High 40
09/05/2017 Low 30
10/05/2017 Med 50
10/05/2017 High 44
Hope this explains what I'm trying to achieve. Please can someone help me with this?
Well as a work-around i have used the Entity Framework Core's "FromSQL" method to execute my stored procedure which take cares of all the GROUP BY's.
you can use this.
data = (from a in context.Calculations
where a.SampleSet.SampleDrawn >= dtStart && (isDeptFilter || a.SampleSet.Department == location)
group a by new { Text = RangeProvider(a.ProbBacteremia * 100, riskCats), Date = a.SampleSet.SampleDrawn.Date } into groupedData
orderby groupedData.Key.Date ascending
select new {
Date = groupedData.Key.Date.ToShortDateString() ,
Low = ( groupedData.Key.Text =="Low" )?groupedData.Count() : 0,
Medium = ( groupedData.Key.Text =="Medium" )?groupedData.Count() : 0,
High = ( groupedData.Key.Text =="High" )?groupedData.Count() : 0,
VeryHigh = ( groupedData.Key.Text =="Very High" )?groupedData.Count() : 0
}).ToList();

multiple aggregates and subquery in slick 3.1

I am trying to translate this sql into a slick 3.1 style collection query (single call). This sql (postgres) returns what I am looking for:
select
minDate.min as lastModified,
(select count("id") from "Items" where "orderId" = 1) as totalItemCount,
(select count("id") from "Items" where "orderId" = 1 and "dateModified" >= minDate.min) as addedCount
from
(select min("dateModified") as "min" from "Items" where "orderId" = 1 and "state" = 'new') as minDate
Returns: for a specified set of Items (from orderId), returns:
date of item last modified
total number of items
number of items added since the lastModified
But after many attempts, I can't figure out how to translate this to a single slick-style query
This codes
import scala.slick.driver.PostgresDriver
case class Item(id: Int, orderId: Int, state: String, dateModified: Int)
object SlickComplexQuery {
def main(args: Array[String]) = {
val driver = PostgresDriver
import driver.simple._
class ItemsTable(tag: Tag) extends Table[Item](tag, "Items"){
def id = column[Int]("id")
def orderId = column[Int]("orderId")
def state = column[String]("state")
def dateModified = column[Int]("dateModified")
def * = (id, orderId, state, dateModified) <> (Item.tupled, Item.unapply)
}
val items = TableQuery[ItemsTable]
val query1 = items
.filter(i => i.orderId === 1 && i.state === "new")
.map(_.dateModified)
.min
val query2 = items
.filter(_.orderId === 1)
.map(_.id)
.length
val query3 = items
.filter(i => i.orderId === 1 && i.dateModified >= query1)
.map(_.id)
.length
val query = Query(query1, query2, query3)
results in such query:
select x2.x3, x4.x5, x6.x7
from (select min(x8.x9) as x3
from (select x10."dateModified" as x9
from "Items" x10
where (x10."orderId" = 1) and (x10."state" = 'new')) x8) x2,
(select count(1) as x5
from (select x11."id" as x12
from "Items" x11
where x11."orderId" = 1) x13) x4,
(select count(1) as x7
from (select x14."id" as x15
from "Items" x14, (select min(x16.x17) as x18
from (select x19."dateModified" as x17
from "Items" x19
where (x19."orderId" = 1) and (x19."state" = 'new')) x16) x20
where (x14."orderId" = 1) and (x14."dateModified" >= x20.x18)) x21) x6
This query is much alike yours, slick 2.0 was used.

Updating multiple tables inside cursor not working

I have two records in a table A. This helps me to update table 2 however the update is not been applied. This is the SQL code where I added a validation section that allow me to determinate the problem.
DECLARE abc CURSOR FOR
select d.cod_suc, d.cod_ramo_comercial, d.nro_pol, d.cod_item, d.cod_ramo_tecnico, d.cod_tarifa,
d.id_stro, d.nro_stro, d.fec_hora_reclamo, d.fec_aviso, d.fec_registro, d.fec_ingreso_contable,
d.txt_place_of_accident, d.id_substro, d.txt_nombre_cober, d.txt_direccion_bien_siniestrado, d.txt_descripcion_perdida, d.txt_cheque_a_nom, d.cod_manager_code, d.importe_pago_eq
from table1 d
where d.fec_hora_reclamo between '20140801' and '20150731'
and (d.cod_suc = 2 and d.cod_ramo_comercial = 255 and d.nro_pol = 1000001 and d.cod_item = 5)
OPEN abc
FETCH abc INTO #cod_suc, #cod_ramo_comercial, #nro_pol, #cod_item, #cod_ramo_tecnico, #cod_tarifa,
#id_stro, #nro_stro, #fec_hora_reclamo, #fec_aviso, #fec_registro, #fec_ingreso_contable,
#txt_place_of_accident, #id_substro, #txt_nombre_cober, #txt_direccion_bien_siniestrado, #txt_descripcion_perdida, #txt_cheque_a_nom, #cod_manager_code, #importe_pago_eq
WHILE (##FETCH_STATUS = 0)
BEGIN
select #varIDPV = min(id_pv), #varCodTarifa = min(cod_tarifa)
from #portfolio p
where p.cod_suc = #cod_suc and p.cod_ramo_comercial = #cod_ramo_comercial and p.Poliza = #nro_pol and p.Item = #cod_item and p.cod_ramo_tecnico = #cod_ramo_tecnico
and p.[ID Incident] IS NULL
/**************************************************************************************************************
-- Validation section
-- First record:
mid(id_pv) = 100, min(cod_tarifa) = 1
-- Second record:
mid(id_pv) = 100, min(cod_tarifa) = 1
--> Should be mid(id_pv) = 100, min(cod_tarifa) = 2
*/
select min(id_pv), min(cod_tarifa)
from #portfolio p
where p.cod_suc = #cod_suc and p.cod_ramo_comercial = #cod_ramo_comercial and p.Poliza = #nro_pol and p.Item = #cod_item and p.cod_ramo_tecnico = #cod_ramo_tecnico
and p.[ID Incident] IS NULL
/**************************************************************************************************************/
update p set p.[ID Incident] = #id_stro, p.[No. Incident] = #nro_stro,
p.[Fecha Accidente] = #fec_hora_reclamo, p.[Fecha Notificacion] = #fec_aviso, p.[Fecha Registro] = #fec_registro, p.[Fecha Pago] = #fec_ingreso_contable,
p.[Lugar Accidente] = #txt_place_of_accident, p.[ID Subsiniestro] = #id_substro, p.[Cobertura Amparo] = #txt_nombre_cober, p.[Direccion Bien Siniestrado] = #txt_direccion_bien_siniestrado, p.[Descripcion Perdida] = #txt_descripcion_perdida, p.[Pago A] = #txt_cheque_a_nom, p.[Referida] = #cod_manager_code,
[Incurrido R12] = #importe_pago_eq, [Incurrido Cerrados R12] = #importe_pago_eq
from #portfolio p
where p.cod_suc = #cod_suc and p.cod_ramo_comercial = #cod_ramo_comercial and p.Poliza = #nro_pol and p.Item = #cod_item and p.cod_ramo_tecnico = #cod_ramo_tecnico
and p.[ID Incident] IS NULL and p.id_pv = #varIDPV and p.cod_tarifa = #varCodTarifa
FETCH abc INTO #cod_suc, #cod_ramo_comercial, #nro_pol, #cod_item, #cod_ramo_tecnico, #cod_tarifa,
#id_stro, #nro_stro, #fec_hora_reclamo, #fec_aviso, #fec_registro, #fec_ingreso_contable,
#txt_place_of_accident, #id_substro, #txt_nombre_cober, #txt_direccion_bien_siniestrado, #txt_descripcion_perdida, #txt_cheque_a_nom, #cod_manager_code, #importe_pago_eq
END
CLOSE abc
DEALLOCATE abc
When I see the result, only the first record is updated.
I found the problem. I forgot to add one condition more in update (inside where). Thanks for the support

How to retrieve unique rows where multiple children that reference it exist for different types?

SELECT * FROM Fruit
INNER JOIN Apple ON Fruit.Id = Apple.FruitId
WHERE Apple.Type = 1 AND Apple.Type = 3
I need to get unique rows of Fruit that have both Apples that are of type 1 AND 3. Apple.Type is considered unique, but I wouldn't think it matters though.
With these rows, this should return two rows with both Fruit #50 and #52. The most important part is the Fruit.Id, I don't need to return the Types, but just need to make sure every single Fruit returned has at least one Apple.Type = 1 and one Apple.Type = 3.
Apple { Id = 1, FruitId = 50, Type = 0 }
Apple { Id = 2, FruitId = 50, Type = 1 }
Apple { Id = 3, FruitId = 50, Type = 3 }
Apple { Id = 4, FruitId = 51, Type = 1 }
Apple { Id = 5, FruitId = 51, Type = 2 }
Apple { Id = 6, FruitId = 52, Type = 3 }
Apple { Id = 7, FruitId = 52, Type = 1 }
Apple { Id = 8, FruitId = 52, Type = 2 }
Fruit { Id = 50 }
Fruit { Id = 51 }
Fruit { Id = 52 }
I'm not quite sure how to use DISTINCT and/or GROUP BY in order to form this query.
Group your apples table by fruit id and pick the results that have both desired types. Use this to get your fruits.
SELECT *
FROM Fruit
WHERE id IN
(
SELECT FruitId
FROM Apple
WHERE Type IN (1,3)
GROUP BY FruitId
HAVING COUNT(DISTINCT Type) = 2
);
This would return the fruits with ID 50 and 52.
SELECT *
FROM Fruit
WHERE EXISTS (
SELECT 1 FROM Apple
WHERE Type = 1 AND Apple.FruitId = Fruit.Id
) AND EXISTS (
SELECT 1 FROM Apple
WHERE Type = 3 AND Apple.FruitId = Fruit.Id
)
Not the most efficient way, but transposing those columns out so you have multiple types per fruitid should do it.
create table type_1 as select FruitId, Type as Type1 from Apple where Type = 1;
create table type_3 as select FruitId, Type as Type3 from Apple where Type = 3;
create table Fruits as select distinct FruitId from Apple;
create table Fruit_Agg as select a.FruitId, b.Type1, c.Type3 from Fruits a left join type_1 b on a.FruitId = b.FruitId left join type_3 c on a.FruitId = c.FruitId;
create table Types_1and_3 as select FruitId from Fruit_Agg where Type1 = 1 and Type3 = 3;