How to optimize LINQ-to-SQL for recursive queries? - sql

I have the following SQL table:
ObjectTable
--------------------------------------------------
| ID | Name | Order | ParentID |
| int PK | nvarchar(50) | int | int FK |
ObjectTable.ParentID is a nullable field with a relationship to a different Object record's ID. LINQ-to-SQL generates an class that looks like:
public class DbObject{
int ID { get; set; }
string Name { get; set; }
int Order { get; set; }
int? ParentID { get; set; }
DbObject Parent { get; set; }
EntitySet<DbObject> ChildObjects { get; set; }
}
When I load a DbObject instance, I need to be able to recursively access the child objects, so that I can write the data to a hierarchical JSON object.
Will I execute a separate query everytime I access the elements via DbObject.ChildObjects? Since DB transactions take the longest, it seems like this is a very inefficient way to load a recursive hierarchy.
What is the best practice for executing a recursive query with LINQ-to-SQL and/or EF?
Is LINQ-to-SQL integrated with Common Table Expressions?
I found Common Table Expression (CTE) in linq-to-sql?

in our case we have created stored procedures with CTE's and put them on the l2s designer and they work like any other Table except they behave as a method rather than property and we have experienced no problem with that so far.

Related

How can I adjust prisma scheme to 'autofil'

I just started using prisma and was wondering whether you could perform an 'autofill'.
For instance: I have a leaderboard, and whenever I list down the teamID, the teamName column gets filled up automatically.
A piece of the schema is as follows.
model LeagueTable {
id Int #id #default(autoincrement())
competitionId Int
teamId Int
played Int
won Int
drawn Int
lost Int
points Int
goalsFor Int
goalsAgainst Int
goalDifference Int
tname String
competition Competitions #relation("competition-lt", fields: [competitionId], references: [id])
team Teams #relation("team", fields: [teamId], references: [id])
}
model Teams {
id Int #id #default(autoincrement())
name String #unique
matchesAsAway Fixtures[] #relation("awayTeam")
matchesAsHome Fixtures[] #relation("homeTeam")
leagueTable LeagueTable[] #relation("team")
}
I was thinking of adding a relation but at the same time I am trying to normalise the scheme as a possible.
Prisma Studio Leaguetable Preview
As you can see the tName column is empty, and I would need to fill it up manually. Is there a way to have it filled up when inserting the teamID
The only way to accomplish this in Prisma is to add another relation as you mentioned.
It might be possible with custom SQL triggers (like before insert), but you would be well outside the Prisma happy path.

Prisma: Create or Connect Records in Explicit Many-to-Many Relations

In my Prisma Schema, I'm finding it difficult to undertand how to to create records in case of explicit many-to-many relations.
I have the following schema. Basically it represents Lists of Books. Users can Create Lists of Books.
A user can create a New list and then add books to this list along with their own notes. The Book Model is pure and contains standard book information.
The extra model is required because the user who is adding the book to the list can add his own notes about the book.
model List {
id Int #default(autoincrement()) #id
title String
slug String?
content String?
published Boolean #default(false)
author User? #relation(fields: [authorId], references: [id])
authorId Int?
books BooksInLists[]
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
model BooksInLists {
list List #relation(fields: [listId], references: [id])
listId Int // relation scalar field (used in the `#relation` attribute above)
book Book #relation(fields: [bookId], references: [id])
bookId Int // relation scalar field (used in the `#relation` attribute above)
##id([listId, bookId])
adder User? #relation(fields: [adderId], references: [id])
adderId Int?
notes String?
}
model Book {
id Int #id #default(autoincrement())
name String
lists BooksInLists[]
curator User? #relation(fields: [curatorId], references: [id])
curatorId Int?
bookDescription String?
}
model User {
id Int #default(autoincrement()) #id
name String?
email String? #unique
lists List[]
books Book[]
booksinlists BooksInLists[]
##map(name: "users")
}
The queries that I want to be able to do.
While updating a list, I should be able to add a new book to the list. This should create the new book, and also allow me to add a new record in BooksInLists model along with the 'notes' field.
While updating a list, I should be able to add/connect an existing book to the list. This will allow me to add a new record in BooksInLists model along with the 'notes' field.
It will be something like that:
prisma.booksInLists.create({
data: {
list: {
connect: {
id: 99
},
},
book: {
create: {
name: 'Young Lions'
}
}
}
})
However I see flaws in database schema. Model BooksInLists connects Books and List, so you don't need adder relation. In turn in Book model you shouldn't add curator relation because it's many to many relation. You have to use junction table usersBooks that connects User and Book tables.

Dynamically write to tables in Dataflow

Working on a pipeline in Dataflow. I need write values to multiple big query table where the desired table names are values in a PCollection.
For example with class Data as:
public class Data{
public List<String> tableName;
public String id;
public String value;
}
I will have a PCollection<Data> and i would like to write row (id, value) into all tables in the list tableNames.
Is this possible in dataflow? Would i be able to use BigQueryIO.Write?

SQL Server 2008R2 Indexes for log table

I have to do some logging in my app. Daily payload is about 50000 insertions. I have to store several fields, the most important - event type and event date/time. There gonna be queries with sorting, paging and filtering. What indexes (on what fields and clustered or non-clusterd) should I create in order to minify insertions and query time (at least for select .. where on the fields above)? Googling give various ideas on the subjuct so I can't figure out what to do
UPD
My POCO:
public class LogEntry
{
public DateTime LoggedAt { get; set; }
public int EventType { get; set; }
public bool IsSuccesful { get; set; }
public string Message { get; set; }
public string URL { get; set; }
public string Login { get; set; }
public string IP { get; set; }
public string UserAgent { get; set; }
}
The most frequent query is select .. where (LoggedAt between .. and ..) and (EventType=..). Sometimes there may be additional and parts of where clause.
Also no update operations are planned. Deletions are possible but only occasionally by lagre bulks.
Following statements is only for ilustrate some possible cases. Ofc its difficult to provide you a specific solution (you have to describe your selectivity). But you can see here some points of view and maybe it can help you.
Some rulles that can help you:
more indexes -> hardest insert
- do best index for majority of selectivity on as much as is possible unique value...
clustered index - replaces your heap by B-Tree
nonclustered index - referencing pages to your heap (creates new object) - consumes more space -> index + data
-- your table should seems like :
CREATE TABLE LogEntry (LoggedAt DATETIME,
EventType INT,
IsSuccesful BIT,
Message VARCHAR(511),--check your input to set it correctly
URL VARCHAR(511),--check your input to set it correctly
Login VARCHAR(127),--check your input to set it correctly
IP VARCHAR(63),--check your input to set it correctly
UserAgent VARCHAR(63))--check your input to set it correctly
-- For examlle for following select
SELECT *
FROM LogEntry
WHERE LoggedAt BETWEEN GETDATE() AND DATEADD(dd,-1,GETDATE()) AND
EventType = 1
-- can help following index (ofc unique values is best for clustered indexes)
CREATE CLUSTERED INDEX idx_LogEntry_LoggedAt_EventType ON dbo.LogEntry (LoggedAt,EventType)
-- For example for following select
SELECT Message
FROM LogEntry
WHERE LoggedAt BETWEEN GETDATE() AND DATEADD(dd,-1,GETDATE()) AND
EventType = 1
-- can help following index
CREATE NONCLUSTERED INDEX idx_LogEntry_LoggedAt_EventType ON dbo.LogEntry (LoggedAt,EventType) INCLUDE (Message)
-- and so ... it really depends what you really want...
-- for me can be really helpfull following solution:
CREATE TABLE LogEntryO (LogEntryId INT IDENTITY PRIMARY KEY CLUSTERED, -- my clustered index
LoggedAt DATETIME,
EventType INT,
IsSuccesful BIT,
Message VARCHAR(511),--check your input to set it correctly
URL VARCHAR(511),--check your input to set it correctly
Login VARCHAR(127),--check your input to set it correctly
IP VARCHAR(63),--check your input to set it correctly
UserAgent VARCHAR(63))--check your input to set it correctly
-- + following index
CREATE NONCLUSTERED INDEX idx_LogEntryO_LoggedAt_EventType ON dbo.LogEntryO (LoggedAt) INCLUDE (LogEntryId)
-- and my query should seems
;WITH CTE AS (SELECT LogEntryId FROM dbo.LogEntryO WHERE LoggedAt BETWEEN GETDATE() AND DATEADD(dd,-1,GETDATE()))
SELECT *
FROM dbo.LogEntryO a
JOIN CTE b ON a.LogEntryId = b.LogEntryId
WHERE a.EventType = 1
It is really hard to create best solution for you, because it seems that you using c# class for accesing to this table. For example you could using some kind of ORM , for example entity framework or soo...
It's difficult to give a specific answers without more information. Any way, I think you can try using an index with your two most important field.
Remember (but may be you already know it) the order of the field is important with respect the query you do.
If you know that the query is always the same, you can add others filed (in the index or with INCLUDE column).
Evaluate the "cardinality" of the fields value too.
If possible, give often a look at the information MSSQL stores about the use of the index.
If it is a OLTP system, with frequent update/delete, it could be not positive to add too much indexes.

NHibernate SaveOrUpdate without primary key

The Situation
I've got a database table that is mapped via NHibernate (3.3.3-SP1). The application is running on .NET4.0 and the mapping is done via FluentNHibernate (1.4.0).
CREATE TABLE Movies
(id INT PRIMARY KEY,
yearPublished DATETIME NOT NULL,
name NVARCHAR(500) NOT NULL,
description NTEXT NOT NULL)
The data would be something like this:
id | yearPublished | name | description
---+---------------+------------------------+--------------------------------------------
1 | 1968 | 2001: A Space Oddyssey | An epic drama of adventure and exploration
The Problem
I'm creating new entities of this table and want to avoid adding more than one entity for the same real world thing. I know that there is Session.SaveOrUpdate and that there is also a way to make it work with composite and natural ids but that's not really what I want since my entities actually have a primary key and I really only need the composite key for making sure that no duplicates are in the DB.
var movie = new Movies
{
yearPublished = 1968,
name = "2001: A Space Oddyssey",
description = "An awesome journey to Jupiter"
};
// Behavior right now:
// Adds a new movie besides the fact that
// the movie is already in the database
// but now has two entries
session.SaveOrUpdate(movie);
Assert.IsTrue(movie.id == 2 && movie.description == "An awesome journey to Jupiter");
// What I really want is to be able to define what
// makes an object unique other than the primary key;
// in this scenario it should look for a combination
// of "yearPublished" and "name"
session.MyAwesomeSaveOrUpdate(movie);
Assert.IsTrue(movie.id == 1 && movie.description == "An epic drama of adventure and exploration");
Is this functionality in place in NHibernate (e.g. through a custom mapping) or do I have fetch the candidates from the DB and do it by hand?
Thanks!
I solve this by adding a unique constraint on the natural key fields in the database and using an exception converter to convert the SQL Server exception into one my application can handle.
public class SqlServerExceptionConverter : ISQLExceptionConverter
{
public Exception Convert(AdoExceptionContextInfo adoExceptionContextInfo)
{
var sqlException = adoExceptionContextInfo.SqlException as SqlException;
if (sqlException != null)
{
// 2601 is unique key, 2627 is unique index; same thing:
// http://blog.sqlauthority.com/2007/04/26/sql-server-difference-between-unique-index-vs-unique-constraint/
if (sqlException.Number == 2601 || sqlException.Number == 2627)
{
// my custom exception
return new UniqueKeyException(sqlException.Message, sqlException);
}
}
return adoExceptionContextInfo.SqlException;
}
}
Another approach I can think of is to query the database for a matching record before the insert but that's not foolproof because a record could be inserted between the select and your insert.