Combine two counting queries into one Boolean query - c#

I can't think of a good way to write this as a single query.
int count1 = query1.Count();
int count2 = query2.Count();
return count1 > count2;
Please note that I'm interested in ways to write a single query that returns a Boolean value and gets evaluated once on the server.
Please note that I'm interested in ways to write this with LINQ, not with SQL.

Try
return query1.Count() > query2.Count();
It's not worth trying to run two unrelated queries in the same server call, but here it is:
SELECT CASE WHEN (SELECT COUNT(*) FROM Products)
> (SELECT COUNT(*) FROM Orders)
THEN CAST(1 as bit)
ELSE CAST(0 AS BIT)
However, I seriously doubt you can get LINQ to create this kind of query. You would have to call a stored procedure instead.

What's wrong with
var result= query1.Count() > query2.Count();
return result; // result is of type bool
EDIT:
If you really want to execute it as one query on the server, you could use DataContext.ExecuteQuery to execute a dynamic query.

Related

C# - SQL - add rowsCount dynamically to data models

I have a code where i do sql query by casting the table model like this:
string sql = string.Format("SELECT * FROM {0}...", tableName...);
and then:
IEnumerable<T> r = dbConn.Connection.Query<T>(sql...);
the thing is if i want to get total rowsCount(of course i can get count on the "r" but if there is a where clause its not possible because i want total count) i have to another query without where.
so i want to remove the second query. i did this in sql query to get rowsCount:
string sql = string.Format("SELECT *, count(*) over() rowsCount FROM {0}...", tableName...);
i can get the rowsCount with this query but since neither one of models has rowsCount i cant access it, is there any suggestions on how i should do it?
Edit:
first query has paging filter by using offset and limit, so i want totalcount not the count of filtered query.
I'm looking to see if there is a way to not use two seperate queries, and get results and also rowsCount by just one query.
You will have to do 2 SQL queries.
Nothing stopping you running them in one SQL block or calling a stored proc with output parameters. So you don't have to make 2 calls but you will need 2 queries at least.
https://www.sqlservertutorial.net/sql-server-stored-procedures/stored-procedure-output-parameters/
If you are worried about performance of a total count just make sure you have an index on the smallest column in that table and it should be mega fast.
The below example return a dataset and an output in one call
DROP PROCEDURE IF EXISTS dbo.DataSetAndOutput
GO
CREATE PROCEDURE dbo.DataSetAndOutput
#YourId BIGINT,
#CountRecords INT OUTPUT
AS
BEGIN
SELECT * FROM YourTable
WHERE Id = #YourId
SET #CountRecords = (SELECT COUNT(YourId) FROM YourTable)
END
GO
-- Test the output
DECLARE #ResultCount INT
EXEC dbo.DataSetAndOutput #YourId= 252452, #CountRecords = #ResultCount OUTPUT
SELECT #ResultCount AS TheCount

Linq tolist() count returns different value from translated sql

I have a situation where the translated sql direct form Visual Studio is returning a different number of records from the Linq. I am expecting 4 items but in the count() of the resulting list I only have 1. This is the case despite creating more records - it always returns 1.
db.DCLVUnknowns.Where(x => x.DCLVUnknownId == Report.DCLid).ToList();
SELECT
[Extent1].[DCLVUnknownId] AS [DCLVUnknownId],
[Extent1].[Gender] AS [Gender],
[Extent1].[Height] AS [Height],
[Extent1].[Weight] AS [Weight],
[Extent1].[Age] AS [Age],
[Extent1].[Race] AS [Race],
[Extent1].[DCLid] AS [DCLid]
FROM [dbo].[DCLVUnknown] AS [Extent1]
Strange thing is I have the same linq expression running fine for other entities and there is no problem. It is consistently happening at the same spot every time.
db.DCLVUnknowns
Is a query of the entire table, not the query for what you want.
If you want to inspect the IQueryable of the full query, try:
var results = db.DCLVUnknowns.Where(x => x.DCLVUnknownId == Report.DCLid);
var theResultSet = results.ToList();
Here results should translate as roughly:
SELECT
[Extent1].[DCLVUnknownId] AS [DCLVUnknownId],
[Extent1].[Gender] AS [Gender],
[Extent1].[Height] AS [Height],
[Extent1].[Weight] AS [Weight],
[Extent1].[Age] AS [Age],
[Extent1].[Race] AS [Race],
[Extent1].[DCLid] AS [DCLid]
FROM [dbo].[DCLVUnknown] AS [Extent1]
WHERE [Extent1].[DCLVUnknownId] = DCLid
Assuming DCLVUnknownId is a PK/ Identity, you should see one result in theResultSet.

SQL INSERT and SELECT into Oracle using a single command with .NET

I'm trying to execute a SQL command that insert a record on a table and returns the generate primary key.
I'm using .NET Core 3.1 and Oracle.ManagedDataAccess.Core package.
This is the C# code to execute the SQL command (it uses some extension methods but is clear how it works):
private int PutSomethingInTheDatabase(string entity)
{
string sqlComamnd = File.ReadAllText("SQL//Insert Card.sql");
using (var connection = new Oracle.ManagedDataAccess.Client.OracleConnection(connectionString))
using (var command = connection.OpenAndUse().CreateTextCommand(sqlComamnd))
{
//var reader = command.ExecuteReader();
//reader.Close();
//var result = command.ExecuteScalar();
//return (int)(decimal)result;
return -1;
}
}
Ideally I will receive a single value and read it with ExecuteScalar().
It is an itegration test (thats why I read the SQL from a file).
The SQL I want to use should INSERT the new record and return the generated sequence within the same scope/transaction, that's whi I'm using Begin/End but I'm not sure it is the right way.
My problem is that I cannot find the right syntax to execute the last SELECT to return the generated sequence_id, I also tried with RETURN...
This is the SQL:
declare new_id number;
BEGIN
select seq_stage_card.NEXTVAL into new_id from dual;
INSERT INTO spin_d.stage_card (
sequence_id,
field_1,
field_2
)
VALUES (
new_id,
'aaa'
TO_DATE('2003/05/03 21:02:44', 'yyyy/mm/dd hh24:mi:ss')
);
select new_id from dual where 1 = 1 ; -- not valid
END;
-- return new_id ; -- not valid
-- select new_id from dual ; -- not valid
How to change the SQL in order to return the new_id ?
There is another (better) way to achieve the same result?
Is it safe (isolated scope), or the select will return a wrong ID if there is a concurrent insert?
[Update]
Someone suggested to use RETURNING (see here: Oracle - return newly inserted key value)
I already tried to use RETURN and RETURNING but I haven't find any real example of usage with the .NET (or other frameworks) driver, eg. OracleSqlCommand and the right call to execute.
Maybe it works but I still cannot figure out how to use it.
In general case (when you have to implement some logics within anonymous block, and when returning is not an option) try bind variables: first, turn new_id into :new_id in the query:
BEGIN
SELECT seq_stage_card.NEXTVAL
INTO :new_id -- bind variable to return back to c#
FROM dual;
INSERT INTO spin_d.Stage_Card (
sequence_id,
field_1,
field_2
)
VALUES (
:new_id,
'aaa',
TO_DATE('2003/05/03 21:02:44', 'yyyy/mm/dd hh24:mi:ss')
);
END;
Then use it in C# code:
...
using (var command = connection.OpenAndUse().CreateTextCommand(sqlComamnd))
{
//TODO: check the syntax and RDBMS type
command.Parameters.Add(
":new_id",
OracleDbType.Int32).Direction = ParameterDirection.Output;
// Execute query
command.ExecuteNonQuery();
// Bind variable reading
return Convert.ToInt32(command.Parameters[0].Value);
}

How to use JSON_VALUE in WHERE clause with type conversion in CodeFirst

I have an order with specific data. Orders stores in dbo.Orders table which have SpecificData field (nvarchar(max)) for json-data.
I use EF Code first. For JSON_VALUE I wrote DBFunction translation using this answer (https://stackoverflow.com/a/50490674/1411598) but I faced with a problem when I tried to add WHERE condition with INT type.
if (specificFilter.RegionIds != null && specificFilter.RegionIds.Count() > 0)
{
query = query.Where(g => specificFilter.RegionIds.Contains(DbFunc.JsonValue(g.specificData, "$.RegionId")));
}
Because JsonValue function return only string I cannot add clause with "contains".
Think a little bit I try to write convert UDF function and translate to EF object.
Here is my code:
if (specificFilter.RegionIds != null && specificFilter.RegionIds.Count() > 0)
{
query = query.Where(g => specificFilter.RegionIds.Contains(DbFunc.ConvertToInt(DbFunc.JsonValue(g.specificData, "$.RegionId"))));
}
UDF is very simple
CREATE FUNCTION [dbo].[ConvertToInt] (#value nvarchar(100))
returns int
AS
BEGIN
return convert(INT, #value)
END
It's works fine, but query is too slow:
WHERE (0 = [Extent1].[Status])
AND ([dbo].[ConvertToInt](JSON_VALUE([Extent1].[specificData], N''$.RegionId'')) IN (1))
It's bad because if I have a lot of rows query works very slow (UDF runs for each row)
So, can you help me to find the solution?
I want to finally get such query:
WHERE (0 = [Extent1].[Status])
AND (CAST(JSON_VALUE([Extent1].[specificData], N''$.RegionId'') as int) IN (1))
By the other words can I translate CAST or CONVER function in EF DBFunction? Or may be there are some other ways?

EF Pre Compile query and return of a scalar value

I use asp.net 4 c# and ef4.
I have this code, it should compile a query and return a single scalar value (I use anonymous type).
My code does not have apparently errors, but because is the first time I write a compiled query I would like to know if is well written or could be improved for a performance boost.
var query = CompiledQuery.Compile((CmsConnectionStringEntityDataModel ctx)
=> from o in ctx.CmsOptions
where o.OptionId == 7
select new
{
Value = o.Value
});
uxHtmlHead.Text = query(context).FirstOrDefault().Value;// I print the result in a Label
SQL Profile Output:
SELECT TOP (1)
[Extent1].[OptionId] AS [OptionId],
[Extent1].[Value] AS [Value]
FROM [dbo].[CmsOptions] AS [Extent1]
WHERE 7 = [Extent1].[OptionId]
Many Thanks
Result after Wouter advice (please guys have a double check again):
static readonly Func<CmsConnectionStringEntityDataModel, int, string> compiledQueryHtmlHead =
CompiledQuery.Compile<CmsConnectionStringEntityDataModel, int, string>(
(ctx, id) => ctx.CmsOptions.FirstOrDefault(o => o.OptionId == id).Value);
using (var context = new CmsConnectionStringEntityDataModel())
{
int id = 7;
uxHtmlHead.Text = compiledQueryHtmlHead.Invoke(context, id);
}
Resulting SQL (I do not understand why with a LEFT JOIN)
exec sp_executesql N'SELECT
[Project1].[Value] AS [Value]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN (SELECT
[Extent1].[Value] AS [Value]
FROM [dbo].[CmsOptions] AS [Extent1]
WHERE [Extent1].[OptionId] = #p__linq__0 ) AS [Project1] ON 1 = 1',N'#p__linq__0 int',#p__linq__0=7
There are 2 things you can improve on.
First, precompiling a query is definitely a good idea but if you have a look at your code you will see that it precompiles the query each and every time instead of only once.
You need to move the precompiled query to a static variable that is initialized only once.
Another thing you need to be careful of is that when precompiling a query you shouldn't modify the query anymore before executing it.
You are building a precompiled query that will select all rows and then you say 'firstordefault' which changes the precompiled query to a SELECT TOP (1) and you lose the benefit of precompiling. You need to move the FirstOrDefault part inside your precompiled query and return only one result.
Have a look at this documentation. If you look at the examples you can see how they use a static field to hold the compiled query and how they specify the return value.

Categories

Resources