SQL Query Parent Child Full Path from table - sql-server
I have a table listing the parent child relationship for each element like this:
ParentID ParentTitle ChildId ChildTitle
----------------------------------------------
843 Documents 38737 Jobs
843 Documents 52537 Tools
843 Documents 5763 SecondOps
843 Documents 4651 Materials
38737 Jobs 16619 Job001
38737 Jobs 16620 Job002
38737 Jobs 16621 Job003
38737 Jobs 16622 Job004
38737 Jobs 16623 Job005
52537 Tools 1952 HandTools
52537 Tools 1953 Automated
52537 Tools 1957 Custom
1952 HandTools 12 Cordless10mm
1952 HandTools 13 Cordless8mm
1952 HandTools 14 CableCrimp
1952 HandTools 15 Cutter
1952 HandTools 16 EdgePlane
5763 SecondOps 101 Procedure001
5763 SecondOps 102 Procedure002
5763 SecondOps 103 Procedure003
4651 Materials 33576 Raw
4651 Materials 33577 Mixed
4651 Materials 33578 Hybrid
4651 Materials 33579 Custom
16622 Job004 101 Procedure001
16622 Job004 14 CableCrimp
16622 Job004 15 Cutter
16622 Job004 4651 Mixed
16623 Job005 102 Procedure002
16623 Job005 103 Procedure003
16623 Job005 16619 Job001
16623 Job005 1953 Automated
16623 Job005 33579 Custom
16623 Job005 33576 Raw
I would like to get the full path of each Combination using the IDs, for example
Documents\Jobs\Job003 = 843\38737\16621
Another example would be "Procedure001" which is listed in 2 places
Documents\SecondOps\Procedure001 = 843\5763\101
The same document is also referenced here:
Documents\Jobs\Job004\Procedure001 = 843\38737\16622\101
I'd like to take this table and build a TreeView in .NET. So having the full path for each item would make it a cake walk.
Otherwise, I was thinking that I could start at the Root page and keep recursing through the parents, building a child list, then recursing those, etc.
Is there a better way to query this to build those paths? This list has 400,000 records so if there is a more efficient way it would save time
This was all originally in an AS400 system DB until 2000ish then made into a MediaWiki site. I am pulling the data via the api with the intent of building an interface for a SQL Server database.
I can do basic SQL queries, joins, unions, etc.
Let me know what other info I can provide if this isn't clear
You could use INNER JOIN and LEFT JOIN if you are using SQL SERVER MS, and here are how the query look like, which will give you the full result (combination) based on your requirement:
SELECT A.ParentTitle + '\'+B.ParentTitle+
CASE WHEN C.ParentTitle IS NOT NULL THEN '\' +C.ParentTitle
ELSE ''
END
+
' =' + A.ParentID + '\'+B.ParentID+
CASE WHEN C.ParentID IS NOT NULL THEN '\' +C.ParentID
ELSE ''
END
FROM TABLE AS A
INNER JOIN TABLE AS B
ON B.ParentID = A.ChildId
LEFT JOIN TABLE AS C
ON C.ParentID = B.ChildId
Not 100% sure whether it will work as I expected or not, please give it a try xD
A tree structure means Recursion for a generic solution.
Pls, don't try this in sql. Just take datarow from sql into a list or something like and make populate with recursion in a programming language.
Your tree class wil be like :
public class MyObj {
public int Id {get; set;}
public string Title {get; set;}
public List<MyObj> {get; set; } = null;
}
0.You table its pretty wrong. The corect way will be :
CREATE TABLE Jobs(
Id int not null primary key,
Title nvarchar(255) not null,
StartTime datetime,--optional maybe will help
ParentId int null --can be null root will have no parent
)
But I will try to explain on your table how it's done.
I will suppose that you have some kind datacontext (DBML,EDMX etc.)
Find root or roots. In your case root will those nr that are on ParentID and are not on the ChildId.
Query that will list your roots:
SELECT DISTINCT a.ParentId FROM
YourTable a LEFT JOIN
YourTable b ON a.ParentId=b.ChildId
WHERE b.ParentId is null
Make a recursive procedure that will retrive your data in a class structure as above(MyObj).
procedure MyObj GetTree(int id, db){
if (db.YourTable.Any(r => r.ParentId==Id)){
var q=db.YourTable.Select(r => r.ParentId==Id).ToList();
var result = new MyObj{
Id = q.ParentId,
Title = q.ParentTitle,
Children = new List<MyObj>()
}
foreach( var el in q) {
if (db.YourTable.Any(r => r.ParentId==el.ChildId))
result.Children.Add(GetTree(el.ChildId,db))
else
result.Children.Add( new MyObj{
Id = el.ChildId,
Title = el.ChildTitle,
Children = null
});
return result;
}
}
return null;
}
make trees with list Id from point 1 stored in a list let's say ListIds you will do something like that:
List finaltrees = new List()
Ids.ForEach(id => finaltrees.Add(GetTree(id,dbcontext));
Now you have a tree structure in finaltrees.
PS:
I wrote the code directly in browser (C#),there can be some typos error.
So to elaborate on what I am trying to do, I'm working with a wiki version that doesn't use namespaces to establish document paths.
For example if a page is 3 levels deep on a document tree like this
RootPage
Page01
Page02
Page03
Page04
Using the Namespace approach Page03's Name(Path) is "RootPage:Page01:Page02:Page03"
I would Like to do the same thing with the PageIDs
So given this example you would have
PageTitle PageId Path
RootPage 001 001
Page01 101 001:101
Page02 201 001:101:201
Page03 301 001:101:201:301
Page04 302 001:101:201:302
So now All I have to do is Put the PagePath together.
There are several challenges to Consider with this wiki
No 2 documents can have the same TITLE
Document IDs are basically
irrelevant, but handy in this case(at least in the version I am
working on)
Thankfully there is a list of Pages and their "Links" or
Child Pages. I believe you would call it a MANY to MANY
The Key Point to remember is even if a page is listed as a child of many other pages, Only one really exists and I only need one of them in the results.
So Using LONG's example here is where I've gotten to
Using this Table:
CREATE Table [dbo].[ExampleTable](
[RecordID] Int IDENTITY (1, 1) Not NULL,
[ParentID] Int Not NULL,
[ParentTitle] VARCHAR(800) NULL,
[ChildID] Int Not NULL,
[ChildTitle] VARCHAR(800) NULL,
PRIMARY KEY CLUSTERED ([RecordID] ASC),);
This Data:
INSERT INTO [dbo].[ExampleTable]
([ParentID]
,[ParentTitle]
,[ChildID]
,[ChildTitle])
VALUES
(843,'Documents',38737,'Jobs'),
(843,'Documents',52537,'Tools'),
(843,'Documents',5763,'SecondOps'),
(843,'Documents',4651,'Materials'),
(38737,'Jobs',16619,'Job001'),
(38737,'Jobs',16620,'Job002'),
(38737,'Jobs',16621,'Job003'),
(38737,'Jobs',16622,'Job004'),
(38737,'Jobs',16623,'Job005'),
(52537,'Tools',1952,'HandTools'),
(52537,'Tools',1953,'Automated'),
(52537,'Tools',1957,'Custom'),
(1952,'HandTools',12,'Cordless10mm'),
(1952,'HandTools',13,'Cordless8mm'),
(1952,'HandTools',14,'CableCrimp'),
(1952,'HandTools',15,'Cutter'),
(1952,'HandTools',16,'EdgePlane'),
(5763,'SecondOps',101,'Procedure001'),
(5763,'SecondOps',102,'Procedure002'),
(5763,'SecondOps',103,'Procedure003'),
(4651,'Materials',33576,'Raw'),
(4651,'Materials',33577,'Mixed'),
(4651,'Materials',33578,'Hybrid'),
(4651,'Materials',33579,'Custom'),
(16622,'Job004',101,'Procedure001'),
(16622,'Job004',14,'CableCrimp'),
(16622,'Job004',15,'Cutter'),
(16622,'Job004',4651,'Mixed'),
(16623,'Job005',102,'Procedure002'),
(16623,'Job005',103,'Procedure003'),
(16623,'Job005',16619,'Job001'),
(16623,'Job005',1953,'Automated'),
(16623,'Job005',33579,'Custom'),
(16623,'Job005',33576,'Raw')
GO
And This Query, Which I modified from LONG's example:
SELECT DISTINCT C.ChildTitle as PageTitle, convert(varchar(20),A.ParentID) + ':' + convert(varchar(20),B.ParentID) +
CASE WHEN C.ParentID IS NOT NULL THEN ':' + convert(varchar(20),C.ParentID)
ELSE ''
END
+
CASE WHEN C.ChildID IS NOT NULL THEN ':' + convert(varchar(20),C.ChildID)
ELSE ''
END
FROM ExampleTable AS A
INNER JOIN ExampleTable AS B
ON B.ParentID = A.ChildId
LEFT JOIN ExampleTable AS C
ON C.ParentID = B.ChildId
ORDER By PageTitle
I get These Results:
PageTitle UnNamed
NULL 16622:4651
NULL 38737:16622
NULL 38737:16623
NULL 52537:1952
NULL 843:38737
NULL 843:4651
NULL 843:52537
NULL 843:5763
Automated 843:38737:16623:1953
CableCrimp 843:38737:16622:14
CableCrimp 843:52537:1952:14
Cordless10mm 843:52537:1952:12
Cordless8mm 843:52537:1952:13
Custom 38737:16622:4651:33579
Custom 843:38737:16623:33579
Cutter 843:38737:16622:15
Cutter 843:52537:1952:15
EdgePlane 843:52537:1952:16
Hybrid 38737:16622:4651:33578
Job001 843:38737:16623:16619
Mixed 38737:16622:4651:33577
Mixed 843:38737:16622:4651
Procedure001 843:38737:16622:101
Procedure002 843:38737:16623:102
Procedure003 843:38737:16623:103
Raw 38737:16622:4651:33576
Raw 843:38737:16623:33576
What I'd like to get is a SINGLE occurance of each page, Regarless of which Parent it happens to be found
Then I can use these Paths to turn the Virtual Tree Structure into an actual Tree Structure.
The Last Issue is that the actual Link List is VERY similar to the example I created, except that it has 400,000 records.
When I run this query against the actual "Link List" it runs for about 17 minutes and runs out of memory.
I've been researching the MAXRECURSION option, but I am still working on it, don't know if that is problem or not.
Related
converting from file path in a column to reference another table with an id number for a filepath
Using Microsoft SQL Server 2012 I've been trying for weeks to get this sorted over my original question for getting a query to work, it does but not correctly. What I want to achieve from this is on the old questions table I'm getting data from has a "filepath" column that the question data refers to from a folder on the local machine this is structured as an example like this: (some columns I haven't included) Old question table QuizQuestionID MasterQuestionID MasterQuestionGUID MasterTypeID MasterDifficultyID MasterCategoryID MasterDecadeID QuizQuestionTypeID QuizQuestionDifficultyID QuizQuestionCategoryID QuizQuestionDecadeI D QuestionText AnswerText FilePath IsEditable IsDeletable IsDeleted IsDifficultyOverridden CreatedDate ModifiedDate AUTO_UseCount AUTO_TieBreakerUsageCount AUTO_TieBreakerLastUsed ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1222 1755 0bee472592ce78e7457d87d7a172ff7b 3 2 7 3 3 2 7 3 Name the singer. David Essex /Sounds/CHORUS David Essex - Tahiti.mp3 False True False False 2014-01-18 12:53:59.000 2014-02-07 12:28:55.000 0 NULL NULL 1223 1756 1df7bd191ef5e31b854c7de5f18982d0 1 2 11 NULL 1 2 11 NULL What is this savoury item? Green Chili /Images/General/Greeen Chili.png False True False False 2014-01-18 15:17:39.000 2014-01-26 19:46:00.000 0 NULL NULL Now in the new question table, a column uses another table called media2 to find the local filepath of files these are structured: New question table id uuid type question answer media created_date modified_date created_user modified_user master_category master_decade master_difficulty is_editable is_deletable multiple_choice choice_1 choice_2 choice_3 choice_4 blur_effect related square_1 square_2 square_3 square_4 tie_breaker ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 8033 07B3D24A-6FFA-40AF-B723-B78C9899D4B4 Audio Name the singer. David Essex 21488 2015-03-23 11:51:31.000 2016-03-08 15:21:48.697 NULL NULL 7 2 1 False False False NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 17395 48E555BD-52D6-4358-89E5-8FEE0F0F3AFD Text What is this savoury item? Green chili 19459 2013-09-10 23:51:35.460 2013-09-13 12:51:53.963 NULL NULL 12 NULL 1 False False NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL Media2 table id UUID name Path Mime/type directory used for Category folder import folder ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 21488 c25183d2-aad7-4c16-8869-9eac711cc39b CHORUS David Essex - Tahiti Quiz C:\media\audio\c25183d2-aad7-4c16-8869-9eac711cc39b.mp3 audio/mp3 Audio Quiz Audio NULL NULL 19459 642db3c3-c531-4818-9b0d-4d7ccd35e0f9 Green chili C:\media\images\642db3c3-c531-4818-9b0d-4d7ccd35e0f9.png image/png Images Quiz Images NULL NULL Just need to add that "UUID" is a random set of numbers the table uses to identify the file name in their folders so we don't get any duplicated files that may be named the same but don't have the same content and are also used by other software apart from the quiz so I can't use this as a reference to create a query. This is the whole code that converts the "old questions table" to the "new questions table" the substring is the part that converts the format from old to new which works fine but to clarify it returns null to the "media" column on any image or sound questions, its supposed to convert the filepath column from the old table against the "media2" table and then import all rows to the new question table with the "media" column referencing the "media2" table for filepaths. --Specify the database, table and columns we want to insert into INSERT INTO NewDatabase.dbo.quizquestions (uuid, [type], question, answer, created_date, modified_date, master_category, master_decade, master_difficulty, is_editable, is_deletable, media) --Get the data from the old database (old_database) and map the media filename to the media2 table in the new database select questions.uuid, questions.newtype, questions.QuestionText, questions.AnswerText, questions.CreatedDate, questions.ModifiedDate, questions.master_category, questions.master_decade, questions.master_difficulty, questions.IsEditable, questions.IsDeletable, media.id as mediaid from ( select NEWID() as uuid, CASE WHEN qqt.TypeName = 'Sound' THEN 'Audio' ELSE qqt.TypeName END AS newtype, qq.QuestionText, qq.AnswerText, CASE WHEN qq.createdDate is not NULL THEN qq.createdDate ELSE GETDATE() END as createdDate, CASE WHEN qq.ModifiedDate is not NULL THEN qq.ModifiedDate ELSE GETDATE() END as ModifiedDate, CASE WHEN cat.id is NOT NULL THEN cat.id ELSE 1 END as master_category, qqdc.QuizQuestionDecadeID as master_decade, qd.id as master_difficulty, qq.IsEditable, qq.IsDeletable, qq.FilePath, SUBSTRING( qq.FilePath, (len(qq.FilePath) - charindex('/', reverse(qq.FilePath)) + 2), (len(qq.FilePath) - charindex('.', reverse(qq.FilePath))) - (len(qq.FilePath) - charindex('/', reverse(qq.FilePath))) -1 ) as fpath -- (len(qq.FilePath) - charindex('.', reverse(qq.FilePath)) as positionoflastdot), --(len(qq.FilePath) - charindex('/', reverse(qq.FilePath)) as positionoflastslash), from Olddatabase.dbo.QuizQuestion qq left join Olddatabase.dbo.QuizQuestionType qqt on qq.QuizQuestionTypeID = qqt.QuizQuestionTypeID left join Olddatabase.dbo.QuizQuestionDifficulty qqd on qq.QuizQuestionDifficultyID = qqd.QuizQuestionDifficultyID left join Olddatabase.dbo.QuizQuestionCategory qqc on qq.QuizQuestionCategoryID = qqc.QuizQuestionCategoryID left join Olddatabase.dbo.QuizQuestionDecade qqdc on qq.QuizQuestionDecadeID = qqdc.QuizQuestionDecadeID left join Olddatabase.dbo.QuizQuestionCategory qqmc on qq.MasterCategoryID = qqmc.MasterQuestionCategoryID left join Newdatabase.dbo.QuizCategories cat on qqc.CategoryName = cat.name left join Newdatabase.dbo.QuizDifficulties qd on qd.id = qq.MasterDifficultyID where qq.MasterCategoryID is not null ) as questions left join Newdatabase.dbo.Media2 media on media.name = replace(fpath, 'quiz','') i cant really change to structure of the tables as the software relies heavily on it, and changing it manually would be painful and time consuming as there are over 2000 audio/images questions i'm just getting my head around SQL databases and queries but this has stopped me dead from progressing. any help changing this above query or creating something entirely different would be greatly appreciated.
Just looking at it, the code seems properly structured. Faced with something like this, I’d “cut down” the query to only the relevant parts, and debug from there, tossing in extra lines like those you currently have commented out to review the parts of the function calls being returned. Pick it part, look at all th details, and you’ll eventually figure out what’s not lining up. The following should work as an initial "minimum query": select qquestions.FilePath ,questions.fpath ,media.id as mediaid from (-- Build the necessary filepath. Running just the subquery can help figure things out too select qq.FilePath ,SUBSTRING( qq.FilePath ,(len(qq.FilePath) - charindex('/', reverse(qq.FilePath)) + 2) ,(len(qq.FilePath) - charindex('.', reverse(qq.FilePath))) - (len(qq.FilePath) - charindex('/', reverse(qq.FilePath))) -1 ) as fpath from Olddatabase.dbo.QuizQuestion qq where qq.MasterCategoryID is not null ) as questions left join Newdatabase.dbo.Media2 media on media.name = replace(fpath, 'quiz','') Mess with this and it should be easier to figre things out. Bonus advice: code like CASE WHEN qq.createdDate is not NULL THEN qq.createdDate ELSE GETDATE() END as createdDate, can be replaced with the shorter and more legible form isnull(qq.createdDate, getdate()) createdDate,
Modeling a non-primary Key relationship
I am trying to model the following relationship with the intent of designing classes for EF code first. Program table: ProgramID - PK ProgramName ClusterCode Sample data ProgramID ProgramName ClusterCode -------------------------------------- 1 Spring A 2 Fall A 3 Winter B 4 Summer B Cluster table: ID ClusterCode ClusterDetails Sample data: ID ClusterCode ClusterDetails --------------------------------- 1 A 10 2 A 20 3 A 30 4 B 20 5 B 40 I need to join the Program table to the Cluster table so I can get the list of cluster details for each program. The SQL would be Select from Programs P Join Cluster C On P.ClusterCode = C.ClusterCode Where P.ProgramID = 'xxx' Note that for the Program table, ClusteCode is not unique. For Cluster table, neither ClusterCode nor ClusterDetail is unique. How would I model this so I can take advantage of navigation properties and code-first?
assuming you have mapped above two tables and make an association between them and you are using C#, you can use a simple join : List<Sting> clustedDets=new ArrayList<String>(); var q = from p in ClusterTable join c in Program on p equals c.ClusterTable select new { p.ClusterDetails }; foreach (var v in q) { clustedDets.Add(v.ClusterDetails); }
How to separate content from my query into 3 distinct columns
I have got the following query, which puts the data that I need (text in the 245, 260 and 300 tags of my table called "bib") into a single column (called "Title"). Instead I would like to have this data separated into three distinct columns, one for each tag. Any suggestions would be very welcome SELECT DISTINCT isbnEX_inverted.isbn, ISNULL(bib.text, CONVERT(varchar(255), bib_longtext.longtext)) AS Title, top_circ_summary.ranking, bib.tag, bib.bib# FROM bib INNER JOIN item ON bib.bib# = item.bib# INNER JOIN isbnEX_inverted ON bib.bib# = isbnEX_inverted.bib# INNER JOIN top_circ_summary ON item.bib# = top_circ_summary.bib# LEFT OUTER JOIN bib_longtext ON bib.bib# = bib_longtext.bib# AND bib.tag = bib_longtext.tag WHERE (isbnEX_inverted.isbn LIKE '%978__________%') AND (top_circ_summary.collection_group = 'jfic') AND (bib.tag in ('245', '520', '300')) order by top_circ_summary.ranking Here's a sample of the (tab separated) output isbn Title ranking tag bib# 9780143307334 a217 p. :bill. ;c21 cm. 1 300 962366 9780143307334 aDiary of a wimpy kid :bthe third wheel /cby Jeff Kinney. 1 245 962366 9780143307334 aTrying to find a partner for the Valentine's Day dance, Greg finds solace in the fact that his best friend Rowley also doesn't have a date, but an unexpected twist might turn his night around. 1 520 962366
How to write this LINQ ( or SQL) query?
I have two tables like this: News Id Title LanguageId Language table: Languages Id Name IsDefault Now with LanguageId as parameter (for example en-US), and the default language is en-GB, I want to get value like this: News Id Title LanguageId 1 1 en-US 2 2 en-US ...remaining en-US news... 10 10 en-GB 11 11 en-GB ...remaining en-GB news, as en-GB is default... 20 20 fr-FR 21 21 fr-FR ...other news in remaining language ... The current solution is using temp table and cursors to fetch out news for each language. Is there any better way to do it?
Finding the right order for languages is the key here (which resembles what you've got): from lang in db.Languages order by lang.LanguageId == languageId descending, lang.IsDefault descending, lang.Name Then add the join: from lang in db.Languages join newsItem in db.NewsItems on lang.LanguageId equals newsItem.LanguageId order by lang.LanguageId == languageId descending, lang.IsDefault descending, lang.Name select new { newsItem.Id, newsItem.Title, lang.Name }
You can use an order by statement to your tsql like below. ORDER BY CASE LanguageId WHEN 'en-US' THEN 0 WHEN 'en-GB' THEN 1 ELSE 2 END
After some works, my LINQ query is: List<News> GetNews(string languageId) { var languages = db.Languages.ToDictionary(l => l.Id, l => l.IsDefault); var news = db.News.Where(<filter condition>).ToList(); //(otherwise LINQ to Entities will not be able to recognize my custom function) news = news.OrderByDescending(n=>n.LanguageId == languageId).ThenByDescending(n=> OrderByLang(a.LanguageId, languages)).ToList(); } private bool OrderByLang(string languageId, Dictionary<string, bool> languages) { return (languages[languageId]); } It works as expected, but it requires to return the whole table to the application layer for paging, which is not very optiomal. Any other suggestions?
LINQ to SQL Take w/o Skip Causes Multiple SQL Statements
I have a LINQ to SQL query: from at in Context.Transaction select new { at.Amount, at.PostingDate, Details = from tb in at.TransactionDetail select new { Amount = tb.Amount, Description = tb.Desc } } This results in one SQL statement being executed. All is good. However, if I attempt to return known types from this query, even if they have the same structure as the anonymous types, I get one SQL statement executed for the top level and then an additional SQL statement for each "child" set. Is there any way to get LINQ to SQL to issue one SQL statement and use known types? EDIT: I must have another issue. When I plugged a very simplistic (but still hieararchical) version of my query into LINQPad and used freshly created known types with just 2 or 3 members, I did get one SQL statement. I will post and update when I know more. EDIT 2: This appears to be due to a bug in Take. See my answer below for details.
First - some reasoning for the Take bug. If you just Take, the query translator just uses top. Top10 will not give the right answer if cardinality is broken by joining in a child collection. So the query translator doesn't join in the child collection (instead it requeries for the children). If you Skip and Take, then the query translator kicks in with some RowNumber logic over the parent rows... these rownumbers let it take 10 parents, even if that's really 50 records due to each parent having 5 children. If you Skip(0) and Take, Skip is removed as a non-operation by the translator - it's just like you never said Skip. This is going to be a hard conceptual leap to from where you are (calling Skip and Take) to a "simple workaround". What we need to do - is force the translation to occur at a point where the translator can't remove Skip(0) as a non-operation. We need to call Skip, and supply the skipped number at a later point. DataClasses1DataContext myDC = new DataClasses1DataContext(); //setting up log so we can see what's going on myDC.Log = Console.Out; //hierarchical query - not important var query = myDC.Options.Select(option => new{ ID = option.ParentID, Others = myDC.Options.Select(option2 => new{ ID = option2.ParentID }) }); //request translation of the query! Important! var compQuery = System.Data.Linq.CompiledQuery .Compile<DataClasses1DataContext, int, int, System.Collections.IEnumerable> ( (dc, skip, take) => query.Skip(skip).Take(take) ); //now run the query and specify that 0 rows are to be skipped. compQuery.Invoke(myDC, 0, 10); This produces the following query: SELECT [t1].[ParentID], [t2].[ParentID] AS [ParentID2], ( SELECT COUNT(*) FROM [dbo].[Option] AS [t3] ) AS [value] FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [t0].[ID]) AS [ROW_NUMBER], [t0].[ParentID] FROM [dbo].[Option] AS [t0] ) AS [t1] LEFT OUTER JOIN [dbo].[Option] AS [t2] ON 1=1 WHERE [t1].[ROW_NUMBER] BETWEEN #p0 + 1 AND #p1 + #p2 ORDER BY [t1].[ROW_NUMBER], [t2].[ID] -- #p0: Input Int (Size = 0; Prec = 0; Scale = 0) [0] -- #p1: Input Int (Size = 0; Prec = 0; Scale = 0) [0] -- #p2: Input Int (Size = 0; Prec = 0; Scale = 0) [10] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1 And here's where we win! WHERE [t1].[ROW_NUMBER] BETWEEN #p0 + 1 AND #p1 + #p2
I've now determined this is the result of a horrible bug. The anonymous versus known type turned out not to be the cause. The real cause is Take. The following result in 1 SQL statement: query.Skip(1).Take(10).ToList(); query.ToList(); However, the following exhibit the one sql statement per parent row problem. query.Skip(0).Take(10).ToList(); query.Take(10).ToList(); Can anyone think of any simple workarounds for this? EDIT: The only workaround I've come up with is to check to see if I'm on the first page (IE Skip(0)) and then make two calls, one with Take(1) and the other with Skip(1).Take(pageSize - 1) and addRange the lists together.
I've not had a chance to try this but given that the anonymous type isn't part of LINQ rather a C# construct I wonder if you could use: from at in Context.Transaction select new KnownType( at.Amount, at.PostingDate, Details = from tb in at.TransactionDetail select KnownSubType( Amount = tb.Amount, Description = tb.Desc ) } Obviously Details would need to be an IEnumerable collection. I could be miles wide on this but it might at least give you a new line of thought to pursue which can't hurt so please excuse my rambling.