Database-side ordering by distance from strings in C# - c#

I'm using the Entity Framework to model an existing SQL Server database. For the moment, I only have read access to the database.
I need to order my query by distance, but the lat/long are stored as strings in the database instead of a spatial type like SqlGeometry. I'm trying to use DbGeometry.FromText on the lat/long within the LINQ query so that I can then call the Distance method on it, but that's not working. I've Googled extensively with no luck.
Here's the code where I've been trying to get it to work:
var point = DbGeometry.FromText("POINT(" + latitude + " " + longitude + ")");
var query = (from a in context.Adjuster
join ac in context.AdjusterContact on a.AdjusterId equals ac.AdjusterId
join z in context.ZipCode on ac.ZipCode equals z.Zip
let p = DbGeometry.FromText("POINT(" + z.Latitude + " " + z.Longitude + ")")
where (a.Active && !a.IsRecordDeleted)
orderby p.Distance(point)
select new AdjusterWithAdjusterContactAndZipCode() { adjuster = a, adjusterContact = ac, zipcode = z })
//.OrderBy(r => DbGeometry.FromText("POINT(" + r.zipcode.Latitude + " " + r.zipcode.Longitude + ")").Distance(point))
.Take(pageSize);

try getting the DbGeometry object like this:
DbGeometry point = DbGeometry.FromText(string.Format("POINT({0} {1})",longitude, latitude), 4326);

Related

SQL query works in EA but doesn't work with C# on the API

I created a C# script that builds a sql query with data from an excel and then queries the EA repository.
sqlquery_part1A = "select obj.object_id from (t_object obj inner join t_connector j on (obj.Object_ID=j.end_Object_ID)) inner join t_object endobj on (j.start_Object_ID=endobj.Object_ID) where obj.alias = '" + adu_alias.Replace("'", "''") + "' and endobj.Name = '" + datastream + "' and obj.Name = '" + adu_name + "'";
int element_count = repository.GetElementSet(sqlquery_part1A, 2).Count;
The query works fine, except when one of the fields coming from excel has break lines. E.g.
"global_sla_l2p_nrt.*\.nc(|\.gz)$ (Product: L2P-SLA NRT),
global_swh_l2p_nrt.*\.nc(|\.gz)$ (Product: L2P-SWH NRT),
global_sla_l2p_stc.*\.nc(|\.gz)$ (Product: L2P-SLA STC)"
When this happens the script finds no entries in the repository. However, if I take the exact same query to EA it returns an entry.
Here is an example of a query:
select obj.object_id from (t_object obj inner join t_connector j on (obj.Object_ID=j.end_Object_ID)) inner join t_object endobj on (j.start_Object_ID=endobj.Object_ID) where obj.alias = 'global_sla_l2p_nrt.*\.nc(|\.gz)$ (Product: L2P-SLA NRT),
global_swh_l2p_nrt.*\.nc(|\.gz)$ (Product: L2P-SWH NRT),
global_sla_l2p_stc.*\.nc(|\.gz)$ (Product: L2P-SLA STC)' and endobj.Name = 'DIS-ING.FTP' and obj.Name = 'L2P Service products'
Can anyone help?
The solution for me was to replace the break line with Environment.NewLine:
sqlquery_part1A = sqlquery_part1A.Replace("\n", Environment.NewLine);

SQL statement that only fetches the newest department which has been allocated

In my database i have the following rows and columns: https://i.imgur.com/ktUZY9d.jpg
My problem is the same employee has 3 different departments, but he is currently only active in 1. How do I change this SQL statement to only include the latest department he is in, which started in 2018 and ends in 2100, as seen by the ALLOCATION_START and ALLOCATION_END?
Query
string agentIdSubQuery = "SELECT DISTINCT " +
"AGENT_ID " +
"FROM " +
"KS_DRIFT.V_AGENT_ALLOCATION " +
"WHERE " +
"LENGTH(AGENT_INITIALS) < 5";
if(queryParams.SnapshotDate.HasValue)
agentIdSubQuery += " AND " + OracleConversion.ToOracleDate(queryParams.SnapshotDate) + " BETWEEN ALLOCATION_START AND ALLOCATION_END";
Update:
Tried alot of different solutions, but it crashes everytime, when i run through the debugger, further Down in this method this Query is causing me to crash:
string sql = "SELECT " +
"age1.* " +
"FROM " +
"KS_DRIFT.V_AGENT_ALLOCATION age1 " +
"INNER JOIN " +
"(" + agentIdSubQuery + ") age2 ON age1.AGENT_ID = age2.AGENT_ID " +
"ORDER BY " +
"AGENT_INITIALS";
Error Message:
{"Error occured during execution of SQL query: SELECT age1.* FROM KS_DRIFT.V_AGENT_ALLOCATION age1 INNER JOIN (SELECT max(DISTINCT AGENT_ID FROM KS_DRIFT.V_AGENT_ALLOCATION WHERE LENGTH(AGENT_INITIALS) < 5 AND '2018-08-15' BETWEEN ALLOCATION_START AND ALLOCATION_END AND (UPPER(AGENT_INITIALS) = 'JKKA')) age2 ON age1.AGENT_ID = age2.AGENT_ID ORDER BY AGENT_INITIALS."}
Also giving me an inner exception:
{"ORA-00904: \"AGE2\".\"AGENT_ID\": ugyldig identifikator"}
Debugging error screeenshot
Order it by the newest start date (descending) and select Top 1!
string agentIdSubQuery = "AGENT_ID " +
"FROM " +
"KS_DRIFT.V_AGENT_ALLOCATION " +
"WHERE " +
"LENGTH(AGENT_INITIALS) < 5 " +
" AND ROWNUM = 1 " +
" ORDER BY ALLOCATION_START DESC";
EDIT, changed Top 1 to Rownum = 1, for Oracle syntax
The table V_AGENT_ALLOCATION contains various departments per agent along with the dates the agent worked there. You want an agent's last department, which you get with Oracle's KEEP LAST. You haven't given us much information on your table, though. Let's say that the department is referenced by an allocation_id:
select
agent_id,
max(id_allocation) keep (dense_rank last order by allocation_start)
as id_current_allocation
from v_agent_allocation
group by agent_id
order by agent_id;
Your error message shows the final generated SQL:
{"Error occurred during execution of SQL query: SELECT age1.* FROM KS_DRIFT.V_AGENT_ALLOCATION age1 INNER JOIN (SELECT max(DISTINCT AGENT_ID FROM KS_DRIFT.V_AGENT_ALLOCATION WHERE LENGTH(AGENT_INITIALS) < 5 AND '2018-08-15' BETWEEN ALLOCATION_START AND ALLOCATION_END AND (UPPER(AGENT_INITIALS) = 'JKKA')) age2 ON age1.AGENT_ID = age2.AGENT_ID ORDER BY AGENT_INITIALS."}
If you format that so that it's readable, you get:
select age1.*
from ks_drift.v_agent_allocation age1
inner join
( select max(distinct agent_id
from ks_drift.v_agent_allocation
where length(agent_initials) < 5
and '2018-08-15' between allocation_start and allocation_end
and (upper(agent_initials) = 'JKKA') ) age2
on age1.agent_id = age2.agent_id
order by agent_initials
Two syntax issues should jump out:
There is a missing closing bracket after max(distinct agent_id (the distinct is also redundant)
The date literal is missing its date keyword - it should be date '2018-08-15' (or better still, a bind variable).
The brackets around (upper(agent_initials) = 'JKKA') are redundant but perhaps they arise from your generator logic and it's easiest to keep them.
I'm not sure how that relates to your 'newest allocated department' requirement, though. Some sample data (not a screenshot) would help.
If you're looking for the current department, you should compare the allocation dates with the current date.
Your query already has this WHERE clause, so it should work just fine for the example provided. But if you might want to specify that you need only ONE row using the ROWNUM special variable clause (assuming it's Oracle).
SELECT DISTINCT AGENT_ID
FROM KS_DRIFT.V_AGENT_ALLOCATION
WHERE LENGTH(AGENT_INITIALS) < 5
AND ALLOCATION_START < CURRENT_DATE AND CURRENT_DATE < ALLOCATION_END
AND ROWNUM = 1

Xamarin sql using left join for an android Tablet app

var db = new SQLiteConnection(dbPath);
try
{
var results = db.Query<Xclass>("SELECT X.COL1, X.COL2, " +
" X.COL3, X.Col4, X.Col5, " +
" A.Col2, A.COL3, B.Col2, C.Col2 " +
"FROM left join Xam X " +
"left join TABLE1 A on X.COL1 = A.COL1 " +
"left join TABLE2 B on X.COL2 = B.COL1 " +
"left join TABLE3 C on X.COL3 = C.COL1 " +
"WHERE X.COL1 ='"+ somevalue +"'");
}
catch (Exception e)
{
// display message
}
XCLASS Contains all the getters and setters.
But it only pulls in the Xam table and not the A, B, or C table values.
I originally had the table name in the Class but took it out to see if that would help.
Any assistance is appreciated. I am trying to avoid linq but if is necessary I will try it.
The instance(s) of Xclass object is created from the results of your query via reflection.
The columns in query's result set are translated to properties of the class
and the setters of hese properties are used to assign their values.
Therefore you must supply some hints to SQlite so it will know which column corresponds to which property. This is done via as keyword.
So, if you want assign the value of A.COL2 column in the result to property named MyCol2Property inside Xclass, you must retrieve it in the query using the syntax
A.Col2 as MyCol2Property

C# ASP.NET MVC 5 How to execute a raw query?

I am trying to execute a raw query using c#.
Here is what I have done
var accounts = conn.Database.SqlQuery<IEnumerable<string>>("SELECT TOP 1 a.* FROM zipcodes_to_accounts AS c " +
"INNER JOIN accounts AS a ON a.id = c.account_id " +
"WHERE c.zip_code = #p0 "+
"ORDER BY a.completed_ll + a.completed_cp ASC", zipcode).ToArray();
Then I want to take the first record and convert it to json object.
if (accounts.Count() > 0)
{
return JsonConvert.SerializeObject( accounts.First() );
}
But the query is giving me an error
The result type 'System.Collections.Generic.IEnumerable`1[System.String]' may not be abstract and must include a default constructor.
The accounts table has some columns that are varchar, datetime, integers. How can I get this query to work?
UPDATED
Converting the IEnumerable to list like advised in the answer it working. but now the JsonConvert is returning an empty object. Here is my current code
//getAccount
public string GetAccount()
{
string zipcode = StringHelpers.StringOrNull(Request["zip_code"]);
if (zipcode != null)
{
var accounts = conn.Database.SqlQuery<List<string>>("SELECT TOP 1 a.* FROM zipcodes_to_accounts AS c " +
"INNER JOIN accounts AS a ON a.id = c.account_id "+
"WHERE c.zip_code = #p0 "+
"ORDER BY a.completed_ll + a.completed_cp ASC", zipcode).ToList();
var firstAccount = accounts.FirstOrDefault();
if (firstAccount != null)
{
return JsonConvert.SerializeObject(firstAccount);
}
}
return "{}";
}
When I debug my code Here is what I see
Not sure what ORM you're using, but the warning is telling you that IEnumerable cannot be constructed as it's an interface. Therefore the SqlQuery method can't know what return type you're expecting.
Try replacing the generic constraint with a concrete type:
var accounts = conn.Database.SqlQuery<string>("SELECT TOP 1 a.* FROM zipcodes_to_accounts AS c " +
"INNER JOIN accounts AS a ON a.id = c.account_id " +
"WHERE c.zip_code = #p0 "+
"ORDER BY a.completed_ll + a.completed_cp ASC", zipcode).ToArray();
You're asking for a IEnumerable<string> which is an interface. You need to pick a class that implements IEnumerable such as a List

Eager loading child collections with the help of Future and HQL

I am trying to load the child collection of a child of the entity I am selecting. I am trying to mimic this way of doing it, basically creating two future queries and then enumerating one of them. This should lead to two queries to the database:
var idd = session.CreateQuery("from ItemDeliveryDetail idd " +
"join fetch idd.ItemDelivery " +
"left join fetch idd.SupplierInvoice " +
"where idd.Id = 21931828")
.Future<ItemDeliveryDetail>();
var spc = session.CreateQuery("from SpecialCondition spc " +
"where spc.ItemDelivery " +
"in (select idd.ItemDelivery " +
"from ItemDeliveryDetail idd " +
"where idd.Id = 21931828)")
.Future<SpecialCondition>();
var result = idd.ToList();
The last line indeed results in two queries to the database. The queries are exactly what I expect (They are rather lengthy and I don't think they are relevant to the question, but if you would like to see them, I pasted them here).
Problem is, the results of those two queries are not combined, i.e. the following enumeration will still query the database for the SpecialConditions of each ItemDelivery:
foreach (var itemDeliveryDetail in result)
{
foreach (var specialCondition in itemDeliveryDetail.ItemDelivery
.SpecialConditions)
{
// Do something
}
}
How to fix that?
Your second query does not load the ItemDelivery.SpecialConditions collections; only an unused list of SpecialConditions.
I agree with Rippo in that using batch-size is usually cleaner and more performant, even if it results in one or two more roundtrips.
That said, your second query should be:
var spc = session.CreateQuery("from ItemDelivery id " +
"join fetch id.SpecialCondition "
"where id in (select idd.ItemDelivery " +
"from ItemDeliveryDetail idd " +
"where idd.Id = 21931828)"
.Future<ItemDelivery>();
One quick win might be to add batch-size=50 to the bag mapping between ItemDelivery and SpecialConditions.
However I suggest you read this blog "Eagerly loading entity associations efficiently with NHibernate" post from Ayende as it might provide you with the answer you are looking for.
You are facing the classic select n + 1 problem here. I would rather have 1 or maybe 2 more trips to the database rather than a big cartesian product resultset. I am sure this will be the most performant route.
You need to use the same base query twice which I think is your problem.
var idd = session.CreateQuery("from ItemDeliveryDetail idd " +
"join fetch idd.ItemDelivery " +
"left join fetch idd.SupplierInvoice " +
"where idd.Id = 21931828")
.Future<ItemDeliveryDetail>();
var spc = session.CreateQuery("from ItemDeliveryDetail idd " +
"join fetch idd.ItemDelivery id " +
"join fetch id.SpecialCondition spc " +
"where idd.Id = 21931828")
.Future<ItemDeliveryDetail>();
var result = idd.ToList();
Yes I realise this may cause a cartesian product for you but I have had good success using this technique.

Categories

Resources