Recursive CTE results in 'types don't match' error on (MS)SQL
See original GitHub issueMy CTE query returns a concatenated string ‘Label’ that contains the parent’s label + the current label, e.g. “parentfolder/currentfolder”. This works well on SQLite, but not on (MS)SQL. There I get the following error:
Types don't match between the anchor and the recursive part in column "Label" of recursive query "CTE".
MsSQL notices that ‘Label’ is once a NVARCHAR(50) and once a NVARCHAR(MAX). Typical solution in SQL is to cast both columns to the same type (e.g. CAST(Label AS NVARCHAR(MAX)), but how to do this with C# and linq2db?
The generated SQL:
-- SqlServer.2012
WITH [CTE]
(
[Id],
[ParentId],
[Level],
[Label]
)
AS
(
SELECT
[c_1].[Id],
[c_1].[ParentId],
0,
[c_1].[Label]
FROM
[TestFolders] [c_1]
WHERE
[c_1].[ParentId] IS NULL
UNION ALL
SELECT
[c_2].[Id],
[c_2].[ParentId],
[r].[Level] + 1,
[r].[Label] + Convert(VarChar(4000), N'/') + [c_2].[Label]
FROM
[TestFolders] [c_2]
INNER JOIN [CTE] [r] ON [c_2].[ParentId] = [r].[Id]
)
SELECT
[c_3].[Id],
[c_3].[Level],
[c_3].[ParentId],
[c_3].[Label],
[p].[Id],
[p].[Label],
[p].[ParentId]
FROM
[CTE] [c_3]
INNER JOIN [TestFolders] [p] ON [p].[Id] = [c_3].[Id]
Stack trace: Microsoft.Data.SqlClient.SqlException
Types don't match between the anchor and the recursive part in column "Label" of recursive query "CTE".
at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at Microsoft.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
at Microsoft.Data.SqlClient.SqlDataReader.get_MetaData()
at Microsoft.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean isAsync, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String method)
at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at Microsoft.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior)
at Microsoft.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior)
at LinqToDB.Data.DataConnection.ExecuteReader(IDbCommand command, CommandBehavior commandBehavior)
at LinqToDB.Data.DataConnection.ExecuteReader(CommandBehavior commandBehavior)
at LinqToDB.Linq.QueryRunner.ExecuteElement[T](Query query, IDataContext dataContext, Mapper`1 mapper, Expression expression, Object[] ps, Object[] preambles)
at LinqToDB.Linq.QueryRunner.<>c__DisplayClass26_0`1.<SetRunQuery>b__0(IDataContext db, Expression expr, Object[] ps, Object[] preambles)
at LinqToDB.Linq.ExpressionQuery`1.System.Linq.IQueryProvider.Execute[TResult](Expression expression)
at System.Linq.Queryable.Count[TSource](IQueryable`1 source)
at TestProject1.UnitTest1.Test1() in D:\Coding\Conx\LinqToDbCteDemo\TestProject1\UnitTest1.cs:line 88
at TestProject1.UnitTest1.Test1() in D:\Coding\Conx\LinqToDbCteDemo\TestProject1\UnitTest1.cs:line 89
at Xunit.Sdk.TestInvoker`1.<>c__DisplayClass48_1.<<InvokeTestMethodAsync>b__1>d.MoveNext() in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\Runners\TestInvoker.cs:line 264
--- End of stack trace from previous location where exception was thrown ---
at Xunit.Sdk.ExecutionTimer.AggregateAsync(Func`1 asyncAction) in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\ExecutionTimer.cs:line 48
at Xunit.Sdk.ExceptionAggregator.RunAsync(Func`1 code) in C:\Dev\xunit\xunit\src\xunit.core\Sdk\ExceptionAggregator.cs:line 90
Steps to reproduce
You can find a small demonstration here: LinqToDbCteDemo (Requires some installation of MS SQL Server)
class CteEntity<TEntity> where TEntity : class
{
public TEntity Entity { get; set; } = null!;
public Guid Id { get; set; }
public Guid? ParentId { get; set; }
public int Level { get; set; }
public string Label { get; set; } = null!;
}
class TestFolder
{
public Guid Id { get; set; }
[MaxLength(50)]
public string? Label { get; set; }
public TestFolder? Parent { get; set; }
public Guid? ParentId { get; set; }
}
await using var db = Context.CreateLinq2DbConnectionDetached();
var q = db.GetCte<CteEntity<TestFolder>>("CTE", cte =>
{
return (Context.Set<TestFolder>()
.ToLinqToDBTable()
.Where(c => c.ParentId == null)
.Select(c =>
new CteEntity<TestFolder>() {Level = 0, Id = c.Id, ParentId = c.ParentId, Label = (string)c.Label}))
.Concat(
Context.Set<TestFolder>()
.ToLinqToDBTable()
.SelectMany(c => cte.InnerJoin(r => c.ParentId == r.Id),
(c, r) => new CteEntity<TestFolder>
{
Level = r.Level + 1,
Id = c.Id,
ParentId = c.ParentId,
Label = r.Label + '/' + c.Label,
})
);
});
Environment details
linq2db version: 3.1.1 Database Server: MS SQL 2018 (localdb) Database Provider: Microsoft.EntityFrameworkCore.SqlServer Operating system: Windows 10 .NET Framework: .Net Core 3.1
Issue Analytics
- State:
- Created 3 years ago
- Comments:6 (4 by maintainers)
Top Results From Across the Web
CTE error: "Types don't match between the anchor and ...
Msg 240, Level 16, State 1, Line 2 Types don't match between the anchor and the recursive part in column "nm" of recursive...
Read more >Types Don't Match between the Anchor and the Recursive ...
An easy way to do this is to concatenate data from previous levels to the current row. -- This statement fails with error:...
Read more >SQL Server Error Messages - Msg 240
SQL Server Error Messages - Msg 240 - Types don't match between the anchor and the recursive part in column "<Column Name>" of...
Read more >SQL Server – CTE – Error – Types don't match between the ...
SQL Server – CTE – Error – Types don't match between the anchor and the recursive part in the column "ColumnName" of recursive...
Read more >String manipulation in Recursive CTE
But SQL doesn't seem to recognize the string concatenation in the second column and returns the error: Types don't match between the anchor...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
We have to do correct conversion automatically. So it is a bug, but if you want you can do almost everything. Just for sample (you do not need that)
Label = r.Label + Sql.Expr<string>(“Convert(NVarChar(MAX), N'/')“) + c.Label
Ran into this one today, trying to compose a path from name fragments in a hierarchical table. If you’re not using
NVARCHAR(MAX)
(which I’m not) you need to wrap the length conversion around the whole expression. I ended up with this:Sample usage: