Using string_split and CROSS APPLY - c#

Is it possible to write this query using LINQ to Entities and not as raw SQL?
SELECT *
FROM Articles
CROSS APPLY string_split(Tags, ',')
WHERE value IN ('programming', 'sql')
The table is something like this
CREATE TABLE Articles(Id int IDENTITY(1,1) NOT NULL PRIMARY KEY, Title nvarchar(max) NOT NULL, Tags nvarchar(max) NOT NULL);
INSERT INTO Articles (Title, Tags) VALUES ('First', 'programming,sql');
INSERT INTO Articles (Title, Tags) VALUES ('Second', 'programming,csharp');
UPDATE: It's possible with used-defined table-valued function that does SELECT ... CROSS APPLY ...
var tags = new[] { "programming", "sql" };
var articles = from a in db.Articles
join awt in db.ArticlesWithTags() on a.Id equals awt.Id
where tags.Contains(awt.Tag)
select a;
Is there a way to call system table-valued functions in EF?

Well of course it can be translated to LINQ:
var res = from a in db.Articles.AsEnumerable()
from tag in a.Tags.Split(',')
select new {
Id = a.Id,
Title = a.Title,
Tags = a.Tags, // you probably don't want this
Tag = tag
};
You, of course, should understand, that Tags.Split(',') wouldn't be translated into string_split directly. It rather be calling SELECT in SQL and then performing string splitting and new object creation in .NET as a result of AsEnumerable() call.

Related

Using 'OR' inside a LinQ join query (adapting SQL into LinQ)

I have a SQL query that basically joins the MyWords table to the MyTranslations table on two keys. Here are my tables for better understanding.
MyWords table:
Id
WordString
LangType
MyTranslations table:
Id
WordColumnAId
WordColumnBId
Please note that WordColumnAIdand WordColumnBId columns are FKs that represents the MyWords.Id.
My SQL query:
SELECT MyTranslations.Id
,WordColumnAId
,WordColumnBId
,MyWords.WordString
,MyWords.LangType
FROM MyTranslations
join MyWords on (MyTranslations.WordColumnAId = MyWords.Id or MyTranslations.WordColumnBId = MyWords.Id)
where MyWords.LangType !=#currentLangType
I know it doesn't make sense to use join on with multiple keys at first glance but I'm trying to a cross-query that would join two tables whether the key is on WordColumnAId or WordColumnBId.
Problem
I'm trying to adapt the above T-SQL query into a LinQ query. The problem is that I can't find my way around LinQ to use two keys in a single join query.
Here's what I've got so far:
from translation in queryableTranslation
join word in _myWordRepository on translation.WordColumnAId equals word.Id // This is where I want to add `or translation.WordColumnBId equals word.Id` but get errors.
where word.LangType != currentLangType
select new QueryResultDto {
MyTranslationId = translation.Id,
WordString = word.WordString,
LanguageType = word.LangType,
WordColumnAId = translation.WordColumnAId,
WordColumnbId=translation.WordColumnbId,
};
I'm very new to the LinQ and trying to learn.
So my question: is there a way to achieve this in LinQ or am I trying the impossible? I'm also open to better approaches.
EF and other LINQ providers should translate query to INNER JOIN when using this syntax:
var query =
from translation in queryableTranslation
from word in _myWordRepository.Where(word => translation.WordColumnAId == word.Id
|| translation.WordColumnBId = word.Id)
where word.LangType != currentLangType
select new QueryResultDto
{
MyTranslationId = translation.Id,
WordString = word.WordString,
LanguageType = word.LangType,
WordColumnAId = translation.WordColumnAId,
WordColumnbId=translation.WordColumnbId,
};

Linq equivalent of aggregate function on multiple tables in one database trip

I have a table function which returns table names and number of entries within that table :
CREATE FUNCTION [dbo].[ufnGetLookups] ()
RETURNS
#lookupsWithItemCounts TABLE
(
[Name] VARCHAR(100),
[EntryCount] INT
)
AS
BEGIN
INSERT INTO #lookupsWithItemCounts([Name],[EntryCount])
VALUES
('Table1', (SELECT COUNT(*) FROM Table1)),
('Table2', (SELECT COUNT(*) FROM Table2)),
('Table3', (SELECT COUNT(*) FROM Table))
RETURN;
END
What would be the Linq equivalent of above simple function? Notice that I want to get the result in one single shot and the speed of the operation is quite important for me. If I realise that the converted linq to sql results in a massive bulky sql with performance hit, I would rather stick to my existing user defined function and forget about the linq equivilant.
You can do that with a UNION query. EG
var q = db.Books.GroupBy(g => "Books").Select(g => new { Name = g.Key, EntryCount = g.Count() })
.Union(db.Authors.GroupBy(g => "Authors").Select(g => new { Name = g.Key, EntryCount = g.Count() }));
var r = q.ToList();
Not an EF guy, and not sure if this would be more performant.
Select TableName = o.name
,RowCnt = sum(p.Rows)
From sys.objects as o
Join sys.partitions as p on o.object_id = p.object_id
Where o.type = 'U'
and o.is_ms_shipped = 0x0
and index_id < 2 -- 0:Heap, 1:Clustered
--and o.name in ('Table1','Table2','Table3' ) -- Include (or not) your own filter
Group By o.schema_id,o.name
Note: Wish I could recall the source of this, but I've used it in my discovery process.

How do I insert multiple records using Dapper while also including other dynamic parameters?

Here is a truncated example of what I'm trying to do:
var stuffTOSave = new List<SomeObject> {
public int OtherTableId { get; set; }
public List<Guid> ComponentIds { get; set; }
};
var sql = #"CREATE TABLE Components( ComponentId uniqueidentifier PRIMARY KEY )
INSERT INTO Components VALUES (#WhatGoesHere?)
SELECT * FROM OtherTable ot
JOIN Components c on c.ComponentId = ot.ComponentId
WHERE Id = #OtherTableId
DROP TABLE Components"
Connection.Execute(sql, stuffToSave);
I know from other SO questions that you can pass a list into an insert statement with Dapper, but I can't find any examples that pass a list as well as another parameter (in my example, OtherTableId), or that have a non-object list (List<Guid> as opposed to a List<SomeObject> that has properties with names to reference).
For the second issue, I could select the ComponentIds into a list to give them a name like:
stuffToSave.ComponentIds.Select(c => new { ComponentId = c })
but then I'm not sure what to put in my sql query so that dapper understands to get the ComponentId property from my list of ComponentIds (Line 7)
I would still like to know the real way of accomplishing this, but I have this workaround that uses string interpolation:
var sql = $#"CREATE TABLE Components( ComponentId uniqueidentifier PRIMARY KEY )
INSERT INTO Components VALUES ('{string.Join($"'),{Environment.NewLine}('", request.ComponentIds)}')
SELECT * FROM OtherTable ot
JOIN Components c on c.ComponentId = ot.ComponentId
WHERE Id = #OtherTableId
DROP TABLE Components"
I'm not worried about SQL Injection since this is just interpolating a list of Guids, but I'd rather avoid this method if possible.

SQL: Nested query does not have appropriate key

In LINQ, I am trying to inner join custom function written for full-text search and an Iqueryable result.
However, I get the following error when I try to to_ret.select(--something--).ToList()
Nested query does not have appropriate key
LINQ Code:
var sql_query = db.search(st);
var to_ret = from ts in sql_query
from t in table
where t.Id == ts.Value select t;
to_ret = to_ret.Include(x => x.table1)
.Include(x=> x.table2.Select(y=> y.table2Col));
to_ret.select(-something-).toList();
SQL Code:
create function [dbo].[search]
(#keywords nvarchar(4000))
returns table
as
return (
select [key] from containstable(tb,(Name,Description),#keywords)
)
Code that works in place of above LINQ Code :
var ids = (from t in table join ts in db.search(st) on t.Id equals ts.Value select t.Id).ToList();
to_ret = to_ret.Where(x => ids.Contains(x.Id));
However, the code that works isn't efficient enough as it eagerly loads all the ids for comparison
Do not join the table using LINQ, it is not effective.
You need to include all the joined functions into [dbo].[search] table valued function (like a view). Then do just call the [dbo].[search] from EF and filter it.
I have mentioned joined Fulltext table valued function here.
Note that fulltext and filtering together in one query could take time, because it is not easy job for query optimizer. Query optimizer selects to perform first fulltext on entire table(s) and then filtering or the opposite way.

EF (LINQ to SQL) - How to query?

I have the concept of a document that has keyword/s. EF abstracted out the document-keyword joining table to an association.
The structure looks like this
Document: ID (PK)
Document_Keyword: DocumentID (PK), Keyword (PK)
Keyword: Keyword (PK)
I have the requirement to return a list of documents where they contain ALL keywords in a string[]
If I was doing this in SQL it would be similar to below
with t as (
select 'keyword1' KEYWORD union
select 'keyword2'
)
select DocumentID,count(*) from [dbo].[Document_Keyword] p
inner join t on p.KEYWORD = t.KEYWORD
group by DocumentID
having count(*) = (select count(*) from t)
Im struggling to form a linq query that will give me the same result.
I have tried the following LINQ statement however it does returns documents that contain 1 or more of the keywords in the array. I require that documents are only returned if ALL keywords match.
var query = (from k in db.KEYWORD
from b in k.DOCUMENT
join q in arrKeywords //array of string[]
on k.KEYWORD equals q
select new Document()
{
Filename = b.FILENAME,
Description = b.TITLE
});
Any ideas?
Cheers
Jeremy
If I get you well you want entries of which all keywords match exactly, i.e. it doesn't have any other keywords. A way too achieve this is
var kwc = arrKeywords.Count();
var query = from k in db.KEYWORD
let kw = k.DOCUMENT.Select(d => d.KEYWORD)
where kw.All(kw1 => arrKeywords.Contains(kw1))
&& kw.Count() == kwc;
The generated query is still much longer than a hand-coded one would be, but I think the database's query optimizer should be able to handle this.

Categories

Resources