Order a 3 level object - c#

I have this scheme
Document
DocumentId
Position
PositionId
DocumentId
Coordonate
CoordonateId
PositionId
Km
Road
RoadId
CoordonateId
Name
I need to order the documents by Road.Name and then by lowest Coordonate.Km. I tried to order them in SQL and EF but with no luck so now I use this code that is very slow:
foreach (var document in documents)
foreach (var position in documentPositions)
{
if (!position.Coordonates.Any())
continue;
var minKm =
position.Coordonates.OrderBy(a => a.Km).FirstOrDefault().Km.Value;
dictionary.Add(document, minKm);
break;
}
var sorted= dictionary.OrderBy(a => a.Key.RoadName).ThenBy(a => a.Value);
document.RoadName is a property that concatenates all the Road.Names that may be in the document.
LE:
I made a sqlfiddle at http://sqlfiddle.com/#!18/90b00/2/0 Hope that helps

You can write a subquery by Coordonates table, using rank function with windows function to get the samllest km, then do join
select Documents.DocumentId,
Documents.Name,
Roads.Name,
MIN(km) km
from Documents
INNER JOIN Positions ON Documents.DocumentId=Positions.DocumentId
INNER JOIN (
select
PositionId,
CoordonateId,
km,
rank() over(partition by PositionId order by Km) rn
from Coordonates
) Coordonates ON Positions.PositionId=Coordonates.PositionId and rn =1
INNER JOIN Roads ON Coordonates.CoordonateId=Roads.CoordonateId
group by Documents.DocumentId,
Documents.Name,
Roads.Name
Order By Roads.Name,km
[Results]:
| DocumentId | Name | Name | km |
|------------|------|------|----|
| 2 | Doc2 | A1 | 10 |
| 1 | Doc1 | A2 | 10 |
| 3 | Doc3 | A2 | 15 |
sqlfiddle

You can find min value without using OrderBy and then FirstOrDefault Instead use Min which should be a little bit faster.
var minKm = position.Coordinates.Min(coord => coord.Km.Value);
Update: maybe your code is slow because position.Coordinates is not loaded from the database and your code does extra database roundtrip for every document which is why it is slow. Please ensure all data is already loaded and the foreach loop is actually causing that slowness. If coordinates data aren't included you can add Include(...) statement into your EF query.

Related

is there a way to use OFFSET and FETCH in MS access or anything similiar in a query, set query has to be used in ASP.NET [duplicate]

Is it possible to emulate the following MySQL query:
SELECT * FROM `tbl` ORDER BY `date` DESC LIMIT X, 10
(X is a parameter)
in MS Access?
While the Access/JET TOP keyword does not directly provide an OFFSET capability, we can use a clever combination of TOP, a subquery, and a "derived table" to obtain the same result.
Here is an example for getting the 10 rows starting from offset 20 in a Person table in ORDER BY Name and Id...
SELECT Person.*
FROM Person
WHERE Person.Id In
(
SELECT TOP 10 A.Id
FROM [
SELECT TOP 30 Person.Name, Person.Id
FROM Person
ORDER BY Person.Name, Person.Id
]. AS A
ORDER BY A.Name DESC, A.Id DESC
)
ORDER BY Person.Name, Person.Id;
Essentially, we query the top 30, reverse the order, query the top 10, and then select the rows from the table that match, sorting in forward order again. This should be fairly efficient, assuming the Id is the PRIMARY KEY, and there is an index on Name. It might be that a specific covering index on Name, Id (rather than one on just Name) would be needed for best performance, but I think that indexes implicitly cover the PRIMARY KEY.
Another way - Let say you want from 1000 to 1999 records in a table called table1 (of course if you have that many records) you can do something like this.
MSSQL
SELECT *
FROM table1 LIMIT 1000, 1999;
MS Access
SELECT TOP 1000 *
FROM table1
Where ID NOT IN (SELECT TOP 999 table1.ID FROM table1);
To break this down
SELECT TOP NumA *
FROM table1
Where ID NOT IN (SELECT TOP NumB table1.ID FROM table1);
UpperLimit = 1999
LowerLimit = 1000
NumA = UpperLimit - LowerLimit + 1
ex. 1000 = 1999 - 1000 + 1
NumB = LowerLimit -1
ex. 999 = 1000 - 1
A better query would be:
SELECT Users.*
FROM Users
WHERE Users.id In
(
SELECT TOP X A.id
FROM [
SELECT TOP Y Users.*
FROM Users
ORDER BY Users.reg_date DESC
]. AS A
ORDER BY A.reg_date ASC
)
ORDER BY Users.reg_date DESC
Where
if((totalrows - offset) < limit) then
X = (totalrows - offset)
else
X = limit
And:
Y = limit + offset
For example, if total_rows = 12, and we set the limit to 10 (show 10 users per page), and the offset is calculated as p * limit - (limit) where p is the number of the current page, hence in the first page (p = 1) we will get: X = 12 and Y = 10, on the second X = 2 and Y = 20. The list of users is ordered by registration date (descending).
Simple and fastest solution.
myTable {ID*, Field2, Filed3...}
Assume your SortOrder contain primary KEY only
SELECT TOP PageItemsCount tb01.*
FROM myTable AS tb01
LEFT JOIN (
SELECT TOP OffsetValue ID FROM myTable ORDER BY ID ASC
) AS tb02
ON tb01.ID = tb02.ID
WHERE ISNULL(tb02.ID)
ORDER BY tb01.ID ASC
SortOrder based on other fields with duplicated values, in this case you must include your primary key in SortOrder as last one.
For exemple, myTable
+-------+--------+--------+
| ID | Field2 | Filed3 |
+-------+--------+--------+
| 1 | a1 | b |
| 2 | a | b2 |
| 3 | a1 | b2 |
| 4 | a1 | b |
+-------+--------+--------+
SELECT TOP 2 * From myTable ORDER BY FIELD2;
+-------+--------+--------+
| ID | Field2 | Filed3 |
+-------+--------+--------+
| 2 | a | b2 |
| 4 | a1 | b |
| 3 | a1 | b2 |
| 1 | a1 | b |
+-------+--------+--------+
SELECT TOP 2 * From myTable ORDER BY FIELD2, FIELD3;
+-------+--------+--------+
| ID | Field2 | Filed3 |
+-------+--------+--------+
| 2 | a | b2 |
| 4 | a1 | b |
| 1 | a1 | b |
+-------+--------+--------+
But if we add ID to sort order [AS LAST IN FIELDS LIST]
SELECT TOP 2 * From myTable ORDER BY FIELD2, ID;
+-------+--------+--------+
| ID | Field2 | Filed3 |
+-------+--------+--------+
| 2 | a | b2 |
| 1 | a1 | b |
+-------+--------+--------+
Final request
SELECT TOP PageItemsCount tb01.*
FROM myTable AS tb01
LEFT JOIN (
SELECT TOP OffsetValue ID FROM myTable ORDER BY Field2 ASC, ID
) AS tb02
ON tb01.ID = tb02.ID
WHERE ISNULL(tb02.ID)
ORDER BY tb01.Field2 ASC, tb01.ID
You can definitely get the the equivalent of "Limit" using the top keyword. See:
Access Database LIMIT keyword
No, JET SQL does not have a direct equivalent. As a workaround, you could add a WHERE clause that selects an ordered/id column between two values.
If possible, you can also use pass-through queries to an existing MySQL/other database.
While TOP in MS-Access can limit records returned, it does not take two parameters as with the MySQL LIMIT keyword (See this question).

View and Combine 2 rows in 1 from same table

I have a problem to view my data as I want,
IDFlight | Dep1 | Des1| Date | IDFlight2 | Dep2 | Des2 | Date | Price
---------+------+-----+-------+-----------+------+------+--------+--------
2 | AYT | PRN |20.3.15| 3 | PRN | AYT | 27.3.15| 150
2 | AYT | PRN |20.3.15| 4 | PRN | AYT | 30.3.15| 150
1 | AYT | PRN |23.3.15| 4 | PRN | AYT | 30.3.15| 150
1 | AYT | PRN |17.3.15| 3 | PRN | AYT | 27.3.15| 150
So search query was with Dates +- 3 days both for 2 flights.
in my case every flight is registered alone in table Flights, each flight has his flight number and his direction, date and pricing(e.g return and one way).
now the problem here is, when user selects the return option, there will be displayed 2 flights in one row, (flight 1 go to destination, flight 2 return from destination) also there is a differences between dates line in the example in the picture.
what I am trying to achieve is displaying data like in example above , that every "one way " record should match with the " return " record. even if the first data is repeated.
I have done a lot of research but no result,
also I tried to do with a view but no success
I tried union no success.
#prmDepDay int, #prmDesDay int, #prmDateDep datetime, #prmFrom int, #prmTo int,
#prmDateRe datetime, #prmFromRe int, #prmToRe int, #prmTotalRe int
AS
BEGIN
DROP TABLE departureflights
SELECT TOP(100) PERCENT
t_flights.idflight,
t_flights.flightnumber,
t_departureairport.depairportname,
t_destinationairport.desairportname,
t_flights.startdate,
t_flights.totalseats
INTO departureflights
FROM t_flights
INNER JOIN t_departureairport
ON t_flights.iddepartureairport = t_departureairport.iddepartureairport
INNER JOIN t_destinationairport
ON t_flights.iddestinationairport = t_destinationairport.iddestinationairport
INNER JOIN t_flightdirections
ON t_flights.iddirection = t_flightdirections.iddirection
WHERE t_departureairport.iddepartureairport = #prmFrom
AND t_destinationairport.iddestinationairport = #prmTo
AND startdate >= Dateadd(day,-#prmDepDay,#prmDateDep)
AND startdate <= Dateadd(day,#prmDepDay,#prmDateDep)
--and TotalSeats>= #prmTotal
ORDER BY t_flights.startdate
DROP TABLE returnflights
SELECT t_flights.idflight,
t_flights.flightnumber AS ReFlightNumber,
t_departureairport.depairportname AS ReDepAirportName,
t_destinationairport.desairportname AS ReDesAirportName,
t_flights.enddate ,
t_flights.totalseats
INTO returnflights
FROM t_flights
INNER JOIN t_departureairport
ON t_flights.iddepartureairport = t_departureairport.iddepartureairport
INNER JOIN t_destinationairport
ON t_flights.iddestinationairport = t_destinationairport.iddestinationairport
INNER JOIN t_flightdirections
ON t_flights.iddirection = t_flightdirections.iddirection
WHERE t_departureairport.iddepartureairport = #prmFromRe
AND t_destinationairport.iddestinationairport = #prmToRe
AND enddate >= Dateadd(day,-#prmDesDay,#prmDateRe)
AND enddate <= Dateadd(day,#prmDesDay,#prmDateRe)
AND totalseats>= #prmTotalRe
ORDER BY t_flights.enddate
If you join the t_flights table with itself you should get both the outgoing and returning flight info in one row.
SELECT journey_out.idflight IDFlight,
journey_out.iddepartureairport Dep1,
journey_out.iddestinationairport Des1,
journey_out.enddate Date1,
journey_return.idflight IDFlight2,
journey_return.iddepartureairport Dep2,
journey_return.iddestinationairport Dep2,
journey_return.enddate Date2
FROM t_flights journey_out
INNER JOIN t_flights journey_return
ON journey_out.iddestinationairport = journey_return.iddepartureairport
AND journey_out.enddate < journey_return.startdate
ORDER BY journey_out.startdate
The first join condition makes sure that the flight is going home from the correct airport, and the second condition makes sure that the return journey does not start before the arrival.
If you want to see the one way options in the same result set as the return options you can change it to a LEFT JOIN instead of an INNER JOIN.

Join two tables where second table is used as a reference for multiple columns of the first table

I have two tables (names are fictitious for this example):
---------------------------------------
MainTable
---------------------------------------
Condition_1 | Condition_2 | Condition_3
---------------------------------------
J | H | N
R | T |
I | |
W | T |
R | M | Q
...and so on...
--------------------------------------
Conditions
--------------------------------------
Condition_Code | Condition_Description
--------------------------------------
A | Description goes here
B | Description goes here
C | Description goes here
D | Description goes here
E | Description goes here
F | Description goes here
G | Description goes here
...and so on...
I'd like to be able to query a DataSet that would have the Condition_Description from the Conditions table as the value of each condition that has a code within the MainTable table.
So that when I look at a specific row:
ds.Tables["Query"].Rows[i]["Condition_1"].ToString();
I would either get an empty string or a description of that condition, if it previously contained a code.
So that for the first row in the above example, it would return the full description of J instead of the character value.
I tried this query:
SELECT * FROM MainTable
LEFT JOIN Conditions AS C1 ON
MainTable.Condition_1 = C1.Condition_Code
LEFT JOIN Conditions AS C2 ON
MainTable.Condition_2 = C2.Condition_Code
LEFT JOIN Conditions AS C3 ON
MainTable.Condition_3 = C3.Condition_Code
...however, I get a missing operator in query expression error.
P.S. It was tough to create the title for this one, since my database experience is limited, so if anyone has a better title, feel free to change it.
If I understood you correctly than maybe something like this would do the trick:
SELECT
(
SELECT FIRST(Condition_Description) FROM Conditions C1 WHERE MT.Condition_1 = C1.Condition_Code
) as C1_Description,
(
SELECT FIRST(Condition_Description) FROM Conditions C2 WHERE MT.Condition_2 = C2.Condition_Code
) as C2_Description,
(
SELECT FIRST(Condition_Description) FROM Conditions C3 WHERE MT.Condition_3 = C3.Condition_Code
) as C3_Description
FROM MainTable as MT
P.s. Not sure what is syntax of first function in access. Also Conditions table design is not very elegant :)

how to get only those which have maximum value in a specified column in asp.net c#

I have a table with "customer_id, date, installment_no, amount" columns. I want to get the information of last installment of each customer_id till today. here installment_no is int type and when a new installment is deposited, the installment_no is increased by 1 in new entry. My table look like:
CS1001 | 12-06-2013 | 1 | 2500
CS1002 | 19-06-2013 | 1 | 1600
CS1001 | 14-07-2013 | 2 | 2500
I want to get a sqlcommand statement for do so.
Group all records by customer_id, then order all customer's records by installment_no, and select only record with max installment_no from each group:
from c in customers
group c by c.customer_id into g
select g.OrderByDescending(x => x.installment_no).First()
Same with pure SQL if you don't use Linq
SELECT c.* FROM Customers c
INNER JOIN (
SELECT customer_id, MAX(installment_no) max_installment
FROM Customers
GROUP BY customer_id
) cmax
ON c.customer_id = cmax.customer_id
AND c.installment_no = cmax.max_installment
var result = list.GroupBy(x=>x.customer_id)
.Select(g=>g.OrderByDescending(y=>y.installment_no).First())
.ToList();

Union on muliple tables in C#

I am using C# and trying to do union on multiple datatables in the code.
Table 1
ID | Value | Value2
-----------------
1 | Tom | Null
-----------------
2 | John | Null
-----------------
...
Table 2
ID | Value | Value2
-----------------
1 | Null | Susie
-----------------
2 | Null | Kim
-----------------
...
And, I want the result table would be something like
TableResult
ID | Value | Value2
-----------------
1 | Tom | Susie
-----------------
2 | John | Kim
-----------------
...
Is there a way I could this?
I'm not sure what datastructure you are using in C#, but you can do this right in your database:
SELECT COALESCE(Table1.ID, Table2.ID) AS ID
,COALESCE(Table1.Value, Table2.Value) AS Value
,COALESCE(Table1.Value2, Table2.Value2) AS Value2
FROM Table1
FULL OUTER JOIN Table2
ON Table1.ID = Table2.ID
I chose a FULL OUTER JOIN here so that items could be missing on EITHER side (I generally expect most people will use a FULL OUTER JOIN about once a year in their careers), as well as an arbitrary choice to always pick the value in Table1 if it existed first, so Table2 would not overwrite something in Table1.
So as an example of how the FOJ works:
Table 1
1,A,NULL
2,B,X
3,NULL,Y
4,D,Z
Table 2
1,NULL,P
2,NULL,Q
3,C,NULL
5,E,W
Output:
1,A,P
2,B,X
3,C,Y
4,D,Z
5,E,W
If they're already in DataTables, you could load them into another table:
DataTable unionTable = new DataTable();
unionTable.Load(table1.CreateDataReader());
unionTable.Load(table2.CreateDataReader());
I think the notion of doing it server side as previously mentioned is a lot better approach though...
Another way to do this in Linq
//Lets say you have two tables (fill the variables from dababase)
IQueryable tab1 = new List().AsQueryable();
IQueryable tab2 = new List().AsQueryable();
//You can create Result table this way
var resultTable = tab1.Select(t1 => new Table1 { t1.ID, t1.Value, tab2.Any(t2 => t2.ID == t1.ID) ? tab2.First(t2 => t2.ID == t1.ID).Value : null });

Categories

Resources