SQL Theory Multiple References - c#

i am designing a database and have theory problem, about which solution works better to run queries, to be faster on microsoft sql server or simply more relational.
GIVEN
Lets say, we have the following Tables:
Congress, Person, Session, Room, and much more.
Don't mind about the given names. These are just some basic standalone entities.
-----------------------------------------------------------------
| Congress | Person | Session | Room |
-----------------------------------------------------------------
| CongressID | PersonID | SessionID | RoomID |
| Name | Name | Name | Name |
| ... | ... | ... | ... |
-----------------------------------------------------------------
Additionally we have a table called "Right". Rights have a name and can define access to something like one or many of the basic entities. Each person can have those rights assigned.
So there are 2 more tables:
Right and PersonRight
---------------------------------
| Right | PersonRight |
---------------------------------
| RightID | PersonRightID |
| Name | PersonID |
| ... | RightID |
| ... | ... |
---------------------------------
SOUGHT-AFTER
Now there is only one thing missing. The way or table that represents the relations to the other entities. I know three different ways that all will work, but i don't have the deep experience to decide which one will be the best.
1. The relational way?
Upgrade: For every new entity, add a new table
Relation: Right 1 : N Entities
Pros: Adding new entities doesn't affect the others in any way, foreign keys to entities
Cons: Many tables with maybe redundant columns like CreatedDate or rowguid.
SQL Example::
select *
from Right r
left join RightCongress rc on r.RightID = rc.RightID
left join RightSession rs on r.RightID = rs.RightID
left join RightRoom ro on r.RightID = ro.RightID
left join Congress ec on rc.CongressID = ec.CongressID
left join Session es on rs.SessionID = es.SessionID
left join Room er on ro.RoomID = er.RoomID
-------------------------------------------------------
| RightCongress | RightSession | RightRoom |
-------------------------------------------------------
| RightCongressID | RightSessionID | RightRoomID |
| RightID | RightID | RightID |
| CongressID | SessionID | RoomID |
| ... | ... | ... |
-------------------------------------------------------
2. The column way?
2.1 The column way 1
Upgrade: For every new entity, add a new column to table "Right"
Relation: Right 1 : 1 Entities
Pros: No new table required, small statement, foreign keys to entities
Cons: Every new entity affect all other rows, only 1:1 relation possible, column count maybe confusing
SQL Example::
select *
from Right r
left join Congress ec on r.CongressID = ec.CongressID
left join Session es on r.SessionID = es.SessionID
left join Room er on r.RoomID = er.RoomID
-----------------
| Right |
-----------------
| RightID |
| Name |
| CongressID |
| SessionID |
| RoomID |
-----------------
2.2 The column way 2
Upgrade: For every new entity, add a new column to table "RightReference"
Relation: Right 1 : N Entities
Pros: 1:N relation, only one new table, small statement, foreign keys to entities
Cons: Every new entity affect all other rows, column count maybe confusing
SQL Example::
select *
from Right r
inner join RightReference rr on r.RightID on rr.RightID
left join Congress ec on rr.CongressID = ec.CongressID
left join Session es on rr.SessionID = es.SessionID
left join Room er on rr.RoomID = er.RoomID
---------------------------------------
| Right | RightReference |
---------------------------------------
| RightID | RightReferenceID |
| Name | RightID |
| ... | CongressID |
| ... | SessionID |
| ... | RoomID |
| ... | ... |
---------------------------------------
3. The reference way
Upgrade: For every new entity, add a new row to RightReference with the new ReferenceTypeID
Relation: Right 1 : N Entities
Pros: Only one new table and dynamic references
Cons: Anonymous references and have always to remember the indexes to build queries, no foreign keys to entities
Explanation: ReferenceID is the primary ID of the referenced entity/row, like of table Congress, Session and so on. So you can't suggest to which table it references. For that reason there is ReferenceTypeID. It points to a translation table called ReferenceType, where every table is stored with an unique id. Maybe it is possible to use the system method OBJECT_ID instead.
SQL Example::
select *
from Right r
inner join RightReference rr on r.RightID = rr.RightID
left join Congress ec on rr.ReferenceID = CongressID and rr.ReferenceType = 1
left join Session es on rr.ReferenceID = SessionID and rr.ReferenceType = 2
left join Room er on rr.ReferenceID = RoomID and rr.ReferenceType = 3
----------------------------------------------------------
| Right | RightReference | ReferenceType |
----------------------------------------------------------
| RightID | RightReferenceID | ReferenceTypeID |
| Name | RightID | Name |
| ... | ReferenceID | ... |
| ... | ReferenceTypeID | ... |
| ... | ... | ... |
----------------------------------------------------------
And now to all the sql experts.
What is the best or better lets say state of the art solution/way/approach to handle this task?
If you have other ways, please let me know.
What i am Looking for is: General Advantages and Disadavantages, SQL-Performance, implementation difficulties with EntityFramework and everything else you know or think about it.
Thanks!

Usually when dealing with relational databases, anything that requires a schema change is a no-no because you have to do potentially dangerous operations on your SQL server, update EF as well as whatever models you may be using and probably redeploy whatever application serves as the frontend for your database.
The SQL Solution
If you're OK with committing a no-no every time a new entity is added or are for some other reason tied to an RDBMS, you have two options:
If you care about your entity(Congress, Session, Room) table schema
Column way #2 is probably the best idea because it separates relational data from actual table data. Make a separate table for the relationships between entities and rights and put an index on every possible entityId. In your example you'd need indices on CongressId, SessionId and RoomId columns.
If you don't care about your entity table schema
Combine all entity tables into one large Entities table with an Id column and an XML column that contains all your actual entity info such as the type. Single relationship between Entities and rights and you're good.
The NoSQL Solution
If you can go this route, it would probably suit the flexible structure you're looking for much better. You will still need to update the code that accesses the document store but judging from your proposal that seems unavoidable unless you have some extraordinarily-flexible-but-error-prone code in place.
You don't need to do schema/EF updates every time a new entity type is added, and you don't need to worry about relationships. Your Person objects will have all their rights nested right inside and will be stored in the document store exactly that way.

Related

How to delete Category and SubCateogries?

I have little Confusion that when we delete Category is it necessary to delete Subcategory related to that if so please help by writing Sql query?Similarly if delete subcategory then deleting Category will also should be deleted as well?
+-------+------------+
| CatID | CatName |
+-------+------------+
| 1 | Seeds |
| 2 | Pesticides |
| 3 | Fertilizer |
+-------+------------+
+----------+---------------+-----------+
| SubCatID | SubCatName | CatID |
+----------+---------------+-----------+
| 1 | Red Seed | 1 |
| 2 | Red Seed | 1 |
| 3 | Red Seed | 1 |
| 4 | Red Seed | 1 |
| 5 | National Worm | 2 |
+----------+---------------+-----------+
You need ON DELETE CASCADE by altering current schema :
ALTER TABLE SubCategory
ADD CONSTRAINT fk_SubCat_CatID FOREIGN KEY (CatID) REFERENCES category(CatID )
ON DELETE CASCADE;
By this when you delete category from category table, the reference data would auto delete. Just make sure drop your current constraint before create new one with ON DELETE CASCADE.
Given you have a Foreign Key defined on this relation, then yes, you have to delete (because childs cannot be orphans) or set null (if you column CatId allows null values) on the CatID column on the Subcategories table because the relational databases uses the referential integrity. You can define the CASCADE option to do it for you automatically.
ALTER TABLE SubCategory
ADD CONSTRAINT FK_SubCategory_Category
FOREIGN KEY (CatId)
REFERENCES Category (CatId)
ON DELETE CASCADE
Check this article for more details.
If you have a FK and want more control what you are deleting, you must provide two sql statements to make it, for sample.
delete from subcategory where catId = 1
delete from category where catId = 1

Copy contents of one table to another table in a different Database

I have a SQL table similar to (call it UserTable)
+--------+-----------+----------+
| UserId | FirstName | LastName |
+--------+-----------+----------+
| 123 | Bob | Smith |
| 456 | John | Doe |
+--------+-----------+----------+
On a different server I have a table (call it UserBackupTable)
+----------+--------+-----------+----------+
| Location | UserId | FirstName | LastName |
+----------+--------+-----------+----------+
| A | 123 | Bob | Smith |
| B | 456 | John | Doe |
+----------+--------+-----------+----------+
The two tables are identical except for the addition of one column (Location). I would like to backup/copy UserTable to UserBackupTable in a better way than
var dataToCopy = userDb.UserTable.Select(user => new UserBackup
{
Location = _location,
FirstName = user.FirstName,
LastName = user.LastName
}).ToList();
backupDb.UserBackups.AddRange(dataToCopy);
This works but isn't very efficient for me when I have 40+ columns to have to manually type out. These are database first models in case that is needed.
Can you not just do this at the database layer, rather than via an ORM, e.g.
SELECT *
INTO [backupDb].[dbo].[UserBackups]
FROM [dbo].[UserTable]
You'll need to modify the above depending how you do it, e.g. incrementally, or recreating the whole backup table each time depending on the size of your data (just make sure you drop the existing table and select into its replacement as part of a transaction) but you could just automate it via SQL Server Agent or something.
Note that SELECT INTO creates a new target table, so that wouldn't be suitable for an incremental backup approach.

Selecting between multiple sql tables

I have two tables: one for a list of students, and one table to map the student's with some playground toys.
When I select a playground toy, I want to be able to see a list of
students with the following restrictions:
A student can only have one type of toy at a time. (A student with basketball(s) can't show up in the list when I select soccer ball).
A student with a specific toy can have multiple of different colors (A student with a yellow basketball can also have the blue ball).
I'm looking to write an SQL query or convert the tables into a C# list which selects from the student's table such that it will return entries which follow the restrictions. I am using MVC framework in C#, and will be calling the query in the controller through a method which has already been written functionally.
Students
+------------+--------------+
| StudentId | name |
+------------+--------------+
| 1 | Bob |
| 2 | Samuel |
| 3 | Tim |
| ...
+------------+--------------+
PlaygroundMap
+-----+-----------------+--------+------------+
| id | name | color | studentid
+-----+-----------------+--------+------------+
| 1 | basket ball | yellow | 1
| 2 | basket ball | blue | 1
| 3 | tennis ball | black | 2
| 4 | tennis ball | red | 2
| 5 | soccer ball | purple | 3
| ...
+-----+-----------------+--------+------------+
I'm still new to SQL, so any help would be greatly appreciated. Thanks!
you will have to make a composite primary key in playgroundmap table for that at database level between name and studentid in playgroundMap table
How about using the below, I think it matches all of the requirements.
SELECT Students.name, PlaygroundMap.Name, PlaygroundMap.color
FROM Students
JOIN PlaygroundMap
ON Students.StudentId = PlaygroundMap
WHERE Students.StudentID <> (
SELECT Students.StudentID
FROM Students
JOIN PlaygroundMap
ON Students.StudentId = PlaygroundMap
WHERE COUNT(PlaygroundMap.name)
GROUP BY PlaygroundMap.Name)
select * from student
where StudentId NOT IN
(select distinct StudentId from PlaygroundMap)
or StudentId IN
(select distinct StudentId from PlaygroundMap where PlaygroundMapName = #playground)

Merge data from two tables from different SQL Server database in C#

I have one central and two client database that have the same structure (identity id). The application allows users to merge data of selected tables from central db with one client db at a time.
For example:
[db_central].[table]:
+-------+-------+
| id | name |
+-------+-------+
| 1 | A |
| 2 | B |
| 3 | C |
+---------------+
[db_client_1].[table]:
+-------+-------+
| id | name |
+-------+-------+
| 3 | D |
+---------------+
[db_client_2].[table]:
+-------+-------+
| id | name |
+-------+-------+
| 3 | E |
+---------------+
Expected result after merging (twice):
[db_central].[table]:
+-------+-------+
| id | name |
+-------+-------+
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | D |
| 5 | E |
+---------------+
Currently, I'm only able to load tables from database.
When user clicks "Manual Sync" button, the app will compare and merge data of selected tables from left to right database or vice versa.
If table doesn't exist, it will create the new one. If table does exist, it will compare and merge data but I don't know what is the best solution to accomplish this task.
Any suggestion would be appreciated.
This seems like a simple sql query (if the databases are on the same server, or if you have a linked server between them) using except...
insert into db_central // Target table
select name from db_client_1 // Source table
except
select name from db_central // Target table
If you have to do it in Linq, then it's very similar:
// Get the list of names to add
var newNames = dbContext.db_client_1.Select(e => e.name).Except(dbContext.db_central.Select(e => e.name));
// Convert the names to a list of entity objects and add them.
dbContext.db_central.Add(newNames.Select(e => new db_central { name = e.name });
dbContext.SaveChanges();
This is assuming you don't want duplicates in db_central
Ideally you should have two columns at table in Central Database
Primary key (with identity enabled)
ChildKey (Primary Key of Child databases)
Primary key column in central database will take care of ordering, and chiild column will give you primary key in respective database

How do a make a query to count which string appears the most in a certain column?

I'm making a program and I need to make a query to the database asking for the string that appears most often in a given column. In this example, its "stringONE".
----------------------------
| ID | Column (string) |
----------------------------
| 1 | stringONE |
----------------------------
| 2 | stringTWO |
----------------------------
| 3 | stringONE |
----------------------------
| 4 | stringONE |
----------------------------
Now I need to take the name of the string that appears the most and put it into a variable string, for example:
string most_appeared_string = sql.ExecuteScalar();
Also, what happens if there is no string that appears the most, rather 2 or more strings that appear the same amount of times, like this:
----------------------------
| ID | Column (string) |
----------------------------
| 1 | stringONE |
----------------------------
| 2 | stringTWO |
----------------------------
| 3 | stringTWO |
----------------------------
| 4 | stringONE |
----------------------------
Thanks ahead.
#KeithS
Do you have an sql-server version of the query because I'm getting some errors when trying it there. Here's a table example of what I'd like to do precisely.
------------------------------------------------
| ID | column1 (string) | author (string) |
------------------------------------------------
| 1 | string-ONE | John |
------------------------------------------------
| 2 | string-TWO | John |
------------------------------------------------
| 3 | string-ONE | Martin |
------------------------------------------------
| 4 | string-ONE | John |
------------------------------------------------
SELECT TOP (1) column1, COUNT(*) FROM table WHERE author='John' ORDER BY ID
It should return "string-ONE" since it appears the most (2) times for the author John. When trying the query in MS-SQL Management Studio though, this is the error I'm getting:
Column 'table.column1' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Nevermind the edit. Thank you.
This is a pretty easy query (in T-SQL at least):
select top 1 Column, Count(*) from Table group by Column order by Count(*) desc
ExecuteScalar, by an implementation detail, will return the string value because it's the first column of the only row in the result set, even though there are two columns. You could also use ExecuteReader to access the number of times that string occurs.
select top (1) SomeCol, count(*) as Row_Count
from YourTable
group by SomeCol
order by Row_Count desc
Also, what happens if there is no string that appears the most, rather
2 or more strings that appear the same amount of times, like this:
In that case, using the above query, you will get one arbitrary row. You can add with ties to get all rows that has the same highest value.
select top (1) with ties SomeCol, count(*) as Row_Count
from YourTable
group by SomeCol
order by Row_Count desc
SELECT max(counted) AS max_counted FROM (
SELECT count(*) AS counted FROM counter GROUP BY date
)
This could do the trick

Categories

Resources