I have a database where I store objects. I have the following (simplified) schema
CREATE TABLE MyObjects
(
UniqueIdentifier Id;
BigInt GenerationId;
BigInt Value;
Bit DeleteAction;
)
Each object has a unique identifier ("Id"), and a (set of) property ("Value"). Each time the value of the property for an object is changed, I enter a new row into this table with a new generation id ("GenerationId", which is monotonically increasing). If an object is deleted, then I record this fact by setting the "DeleteAction" bit to true.
At any point in time (generation), I would like to retrieve the state of all of my active objects!
Here's an example:
Id GenerationId Value DeleteAction
1 1 99 false
2 1 88 false
1 2 77 false
2 3 88 true
Objects in generations are:
1: 1 {99}, 2 {88}
2: 1 {77}, 2 {88}
3: 1 {77}
The key is: how can I find out the row for each unique object who's generation id is closest (but not exceeding) to a given generation id? I can then do a post-filter step to remove all rows where the DeleteAction field is true.
This works in MS SQL
SELECT id,value
FROM Myobjects
INNER JOIN (
SELECT id, max(GenerationID) as LastGen
FROM MyObjects
WHERE GenerationID <= #Wantedgeneration
Group by ID)
On GenerationID = LastGen
WHERE DelectedAction = false
My version uses a joint of the table MyObjects against a
subset of itself, created by a subquery, and containing only the last
generation for each object:
SELECT O.id,generation,value FROM
MyObjects O,
(SELECT id,max(generation) AS max_generation FROM MyObjects
WHERE generation <= $GENERATION_ID GROUP BY id) AS TheMax WHERE
TheMax.max_generation = generation AND O.deleted is False
ORDER BY generation DESC;
In the above query, the GENERATION_ID is hardwired. A way to
parametrize it is to write a function:
CREATE OR REPLACE FUNCTION generation_objects(INTEGER) RETURNS SETOF MyObjects AS
'SELECT O.id,generation,value,deleted FROM
MyObjects O,
(SELECT id,max(generation) AS max_generation FROM MyObjects
WHERE generation <= $1 GROUP BY id) AS TheMax WHERE
TheMax.max_generation = generation AND O.deleted is False;'
LANGUAGE SQL;
Now, it works. With this table:
> SELECT * FROM MyObjects;
id | generation | value | deleted
----+------------+-------+---------
1 | 1 | 99 | f
2 | 2 | 88 | f
1 | 3 | 77 | f
2 | 4 | 88 | t
3 | 5 | 33 | f
4 | 6 | 22 | f
3 | 7 | 11 | f
2 | 8 | 11 | f
I get:
> SELECT * FROM generation_objects(1) ORDER by generation DESC;
id | generation | value | deleted
----+------------+-------+---------
1 | 1 | 99 | f
> SELECT * FROM generation_objects(2) ORDER by generation DESC;
id | generation | value | deleted
----+------------+-------+---------
2 | 2 | 88 | f
1 | 1 | 99 | f
> SELECT * FROM generation_objects(3) ORDER by generation DESC;
id | generation | value | deleted
----+------------+-------+---------
1 | 3 | 77 | f
2 | 2 | 88 | f
And then, at the following generation, object 2 is deleted:
> SELECT * FROM generation_objects(4) ORDER by generation DESC;
id | generation | value | deleted
----+------------+-------+---------
1 | 3 | 77 | f
Here's the working version:
SELECT MyObjects.Id,Value
FROM Myobjects
INNER JOIN
(
SELECT Id, max(GenerationId) as LastGen
FROM MyObjects
WHERE GenerationId <= #TargetGeneration
Group by Id
) T1
ON MyObjects.Id = T1.Id AND MyObjects.GenerationId = LastGen
WHERE DeleteAction = 'False'
Not sure whether that's standard SQL, but in Postgres, you can use the LIMIT flag:
select GenerationId,Value,DeleteAction from MyObjects
where Id=1 and GenerationId < 3
order by GenerationId
limit 1;
Related
I have a table tbTest like this:
q1 | q2 | q3 | type
--------------------
2 | 1 | 3 | student
3 | 2 | 1 | alumni
2 | 1 | 3 | alumni
1 | 1 | 3 | student
Now I want a new table which is based on the first table and finds the sum of every question by GroupWise convert it into like this:
q | student | alumni
---------------------
q1 | 3 | 5
q2 | 2 | 3
q3 | 6 | 3
SELECT Student,
Alumni
FROM
(SELECT q1, userType FROM tbTest2) tb1
PIVOT
(
SUM(q1)
FOR userType IN (Student, Alumni)
) AS tb2;
But using(above SQL) Pivot I can manage only one row like this:
student | alumni
---------------------
3 | 5
You can unpivot the data and aggregate. Based on the C# tag, I am assuming the database is SQL Server, in which case you can use apply:
select v.question,
sum(case when t.type = 'student' then val else 0 end) as student,
sum(case when t.type = 'alumni' then val else 0 end) as alumni
from t cross apply
(values ('q1', t.q1), ('q2', t.q2), ('q3', t.q3)) v(question, val)
group by v.question;
In other database, you can do something similar using a lateral join or union all.
I have an issue in insertion of records from one table to another the tables schema is same
Table 1
Id Parent Id Text
11 Null A
12 Null B
13 11 C
14 11 D
15 13 E
The record should be inserted in this format, I have to copy the foreign key relation not the identities of it.
Table 2
Id Parent Id Text
31 Null A
32 Null B
33 31 C
34 31 D
35 33 E
What you want could be achieved easily in the Database side.
But since you are looking for a linq approach. Here is how it can be achieved.
The Process is divided into two parts:
Hoping that you would have the model for the Table1 and Table2 something like(use the order you think is helpful. For my thinking the Table1 is the temp table):
Public class myEntity
{
public int Id{get; set;}
public int? ParentId{get; set;}
public string Text{get; set;}
}
1st: Copying all the Text Properties from Table A and inserting them into Table B with incrementing the IDs
var Table2 = new List<myEntity>();
Table1.Select(s=>s.Text).OrderBy(o=>o).ToList().ForEach(f=>
{
//now append the texts to
Table2.Add(new tablesTest { Id = (Table2.Count + 1), Text = f });//remove the Id property manupulation or set it to 0 if you are inserting directly in the database and use the context.SaveChanges();(*if entity-framework*) once the insertion is complete.
}}
2nd: Creating a mapping table using self-join to get the parent-child relationship between the entries from Table1 and then updating the entries in Table2
var parentChildListFromTb1 = from m in Table1
join ch in Table1 on m.Id equals ch.ParentId
select new
{
Id = ch.Id,
Parent = m.Text,
Text = ch.Text
};
Which will give you an output:
----------------------------
| Id | Parent | Text |
----------------------------
| 13 | A | C |
----------------------------
| 14 | A | D |
----------------------------
| 15 | C | E |
----------------------------
Now after we have the parent-child list now we create the child list by querying Table2 and updating its ParentId with their respected Ids:
parentChildListFromTb1.ForEach(f=>{
var ChildEntity = Table2.Single(s => s.Text.Equals(f.Text));//fetching the child entity from Table2
ChildEntity.ParentId = Table2.Single(s => s.Text.Equals(f.Parent)).Id;//updating the parentIds in Table2
});
And the Table2 will look something like :
------------------------------
| Id | ParentId | Text |
------------------------------
| 1 | null | A |
------------------------------
| 2 | null | B |
------------------------------
| 3 | 1 | C |
------------------------------
| 4 | 1 | D |
------------------------------
| 5 | 3 | E |
------------------------------
I have found a solution to this question.
Steps:
Insert the Text column values into the main table, leaving the ParentId as null and at the same time insert the values into a Dictionary as oldId key and New Inserted Id as value to the corresponding key.
After insertion Update the values on the basis of the mapped Dictionary keyValue Pair
I have three data tables that basically contain the following data:
table 1:
ID FContractID | WaitingTime
1 | 1 | 85
2 | 1 | 98
3 | 1 | 122
4 | 1 | 45
5 | 1 | 234
6 | 1 | 101
etc.
Table 2:
PricingCriterionItemId PricingCriterionName PricingCriterionUnit
1 | WaitingTimeMax | min
2 | WaitingTimePeriod | min
3 | WaitingTimeOverdue | €/period
4 | OverDuePriceMax | €
Table 3:
PricingCriterionId ContractID PricingCriterionItemId PricingCriterionValue
1 | 1 | 1 | 70
2 | 1 | 2 | 30
3 | 1 | 3 | 30,00
4 | 1 | 4 | 120,00
I want to add to the table 1 a column that contains waiting time cost. The waiting time cost would be calculated like
WaitingTimeCost = min(((WaitingTime - WaitingTimeMax) / WaitingTimePeriod) * WaitingTimeOverdue, OverDuePriceMax)
I can easily join tables 2 and 3 into one table:
Table 4
PricingCriterionId ContractID PricingCriterionName PricingCriterionValue PricingCriterionUnit
1 | 1 | WaitingTimeMax | 70 | min
2 | 1 | WaitingTimePeriod | 30 | min
3 | 1 | WaitingTimeOverdue | 30,00 | €/period
4 | 1 | OverDuePriceMax | 120,00 | €
Is it possible using linq to assign a column's value on a certain row using other columns value?
Something like
var result = from WaitingData in table1
join PricingCriteria in table4
on WaitingData.ContractId equals PricingCriteria.ContractId
let WaitingTimeMax = (select PricingCriterionValue from table4 where PricingCriterionName = "WaitingTimeMax")
let ...
let WaitingTimeCost = min(((WaitingTime - WaitingTimeMax) / WaitingTimePeriod) * WaitingTimeOverdue, OverDuePriceMax)
select new
{
ID,
WaitingTimeCost
}
How to formulate this properly using linq?
You don't need to do everything in the database query.
Database is only IO device, which responsible only for reading and writing data.
Load all required data and calculate.
var contractsId = waitingData.Select(data => data.ContractId).ToList();
var pricingCriterias = table4.Where(criteria => contractsId.Contains(criteria.ContractId)
.ToLookup(criteria => criteria.ContractId);
var maxWaitingTime =
pricingCriterias.SelectMany(group => group)
.Where(criteria => criteria.PricingCriterionName = "WaitingTimeMax")
.Max(criteria => criteria.PricingCriterionValue);
foreach (var waitingItem in waitingData)
{
// Calculate others waiting values
var waitingPerPeriod = (WaitingTime - maxWaitingTime) / WaitingTimePeriod);
var waitingPrice = waitingPerPeriod * WaitingTimeOverdue;
var WaitingTimeCost = Math.Min(waitingPrice, OverDuePriceMax)
}
Below example with DataTables.
However DataTable is heavy and not strongly typed data structure and as developer it takes more time to deal with it.
Instead transform data in DataTable to the plain strong typed objects with descriptive property names - you will get IntelliSense for free ;)
var contractsId =
waitingData.AsEnumerable()
.Select(row => row.Field<int>("ContractId"))
.ToList();
var pricingCriterias =
table4.AsEnumerable()
.Where(row => contractsId.Contains(row => row.Field<int>("ContractId"))
.ToLookup(row => row.Field<int>("ContractId"));
var maxWaitingTime =
pricingCriterias.SelectMany(group => group)
.Where(row => row.Field<string>("PricingCriterionName") = "WaitingTimeMax")
.Max(row => row.Field<int>("PricingCriterionValue"));
foreach (var waitingItem in waitingData)
{
// Calculate others waiting values
var waitingPerPeriod = (WaitingTime - maxWaitingTime) / WaitingTimePeriod);
var waitingPrice = waitingPerPeriod * WaitingTimeOverdue;
var WaitingTimeCost = Math.Min(waitingPrice, OverDuePriceMax)
}
I have multiple tables and i have a search ASP.NET page with a GridView for the results.
The GridView must contain Name, School, State, Country
I have multiple tables that only contains the data:
index_States
indexID| State
----------------
1 | state1
2 | state2
3 | state3
index_Contries
indexID| Country
----------------
1 | country1
2 | country2
3 | country3
index_Schools
indexID| School
----------------
1 | school1
2 | school2
3 | school3
Then i have the tables that contains the indexID as reference
General_Info
idKey | Name
--------------
1 | John
2 | McClane
3 | Jr.
Academic_XP
id | idSchool | idState | idCountry | idKey
--------------------------------------------
1 | 1 | 3 | 20 | 2
2 | 1 | 5 | 146 | 3
3 | 2 | 1 | 65 | 9
And THEN I have the table that contains UserType as only certain type of user will be searched
Users
id | UserType | idKey
-----------------------
1 | 1 | 1
2 | 3 | 2
3 | 3 | 3
4 | 1 | 4
I've already tried multiple queries but none seem to be working.
Last query that seem to be working was with INNER JOIN
SELECT Name, State
FROM General_Info A, Academic_XP B
INNER JOIN index_States D ON B.idState = D.indexID
GROUP BY A.id;
but it doesn't work as soon as I add a second INNER JOIN or a WHERE clause.
SELECT Name, State
FROM General_Info A, Academic_XP B, Users
INNER JOIN index_States D ON B.idState = D.indexID
INNER JOIN index_School E ON B.idSchool = E.indexID
GROUP BY A.id
WHERE Users.UserType = 3;
I don't know how can I do that.
So i guess the question is
How can I made a query that returns from all those tables something like this?
Name | State | School | Country
---------------------------------------
McClane | state3 | school1 | country20
Jr. | state1 | school5 | country146
Note that McClane and Jr. are both UserType 3.
I will appreciate any help.
You are producing a cartesian product between tables without joins. I think this is what you're looking for using additional JOINs:
SELECT DISTINCT
G.Name,
S.State,
C.Country,
SC.School
FROM Academic_XP A
JOIN Users U ON A.idKey = U.idKey
JOIN General_Info G ON A.id = G.idKey
JOIN Index_States S ON A.idState = S.indexID
JOIN Index_Contries C ON A.idCountry = C.indexID
JOIN Index_Schools SC ON A.idSchool = SC.indexID
WHERE U.UserType = 3
If some tables don't have matching keys, you'll need to use an OUTER JOIN.
A Visual Explanation of SQL Joins
I am trying to convert an MSSQL statement into LINQ. The overall functionality is broken up into many LINQ statements which are combined together, so the result must return type IQueryable.
Included in this question is an example of the schema and data. I am trying to see if the most recent (by DateCreated) value of Number is equal to 400, if so return the Table1Id.
The following is the MSSQL statement.
select * from Table1 t1
where 400 in (
select top 1 t2.Number
from Table2 t2
where t2.Table1Id = t1.id
order by t2.DateCreated desc
)
The LINQ I have come up with so far is:
//initial query
var query1 = _table1Repository.Table
//chain our query
var query2 = from t1 in query1
where ((from t2 in _table2Repository.Table
where t2.Table1Id == t1.Id
orderby t2.DateCreated descending
select t2.Number)
.Take(1)).Contains(400)
select t1;
//execute the query (may be more queries between last query and this)
var queryResult = query2.ToList();
Though upon executing the following error is thrown:
Unable to create a constant value of type 'Project.Domain.Table2'. Only primitive types or enumeration types are supported in this context.
Table 1 data
| Id |
|----|
| 1 |
| 2 |
| 3 |
Table 2 data
| Id | DateCreated | Table1Id | Number |
|----|------------ |-----------|---------|
| 1 | 1/1/2014 | 1 | 100 |
| 2 | 2/1/2014 | 1 | 200 |
| 3 | 3/1/2014 | 1 | 300 |
| 4 | 1/1/2014 | 2 | 200 |
| 5 | 2/1/2014 | 2 | 300 |
| 6 | 3/1/2014 | 2 | 400 |
| 7 | 1/1/2014 | 3 | 400 |
| 8 | 2/1/2014 | 3 | 300 |
| 9 | 3/1/2014 | 3 | 200 |
Expected result
| Id |
|----|
| 2 |
Assuming you still want to use the In keyword, the Lambda version would look like:
var intList = new List<int>(1) { 400 };
var test = _table1Repository.Table
.Where(t1 => intList.Contains(_table2Repository.Table
.Where(t2 => t2.Table1Id == t1.id)
.OrderByDescending(t2 => t2.Number)
.First()));
The only Caveat is that if there are no values in Table2, First() with throw an exception.
An example of mix'd Linq and Lambda:
var query1 = _table1Repository.Table
.OrderBy(t1 => t1.id);
var query2 = (from t1 in query1
select new { Id = t1.Id, Number = number } );
var query3 = query2.ToList();