SQL 3 table structure ascending order - c#

I have 3 tables in my database, “Doctors”, “Workers”, “Works”, I need to store the differentiated price list as well, but I don’t know what would be the perfect solution or placement for them. (I’m writing a basic program for storing the prices for works for a dental company, and listing them)
I know the price placement would be great inside any of these tables, if I don’t need to rearrange the doctor or work list every time the admin adds a new item to the work table or adds a new doctor to the list.
So in short: what’s the best placement of price list in a database, if I have to rearrange the works by ascending order.
For example: I store 3 doctors (d1,d2,d3), and 1 worker (w1), I have works (a,b,c,...) all the works have DIFFERENT prices for DIFFERENT doctors, (and workers). Now I place an a2 work inside works table, I have to rearrange it in ascending order.
Do I need an entirely new structure for database? Any tips? Thx
Already tried to place the price list inside doctors and workers, but the problem is, I have to get them rearrange every time someone adds a new work.
The SQL code wouldn’t make anything easier, I think the example above a better way to show the problem.

If I were you I'd make a change to your existing tables to abstract doctors and workers into Employees and add a fourth table to hold the price for each employee and service (or work as you called it). Here's how I'd do it:
CREATE TABLE [Employees] (
[EmployeeId] int,
[Name] varchar(100),
[RoleId] int
)
CREATE TABLE [Roles] (
[RoleId] int,
[Name] varchar(100)
)
CREATE TABLE [Services] (
[ServiceId] int,
[Name] varchar(100)
)
CREATE TABLE [Employees_Services] (
[EmployeeId] int,
[ServiceId] int,
[Price] decimal(19,4)
)
Employees would have a row for each person in the organization.
Roles would contain, based on your example, two rows, one for Doctor and one for Worker.
Services would contain a row for each type of work that is done by any employee.
Employees_Services would hold a row for each employee and service that they can provide and their price for that service.
This way each employee can have their own price for each service, and adding or removing employees, roles, or services wouldn't require any rearranging. It also gives you the added benefit of being able to control which employee is able to provide each service. E.g. an employee other than a Doctor probably shouldn't be able to provide surgery.
I would also create foreign key constraints between the related columns, I think that part is self-explanatory, but let me know if you need help.

Related

How to handle invalid user input table name

I am writing a C# WinForms program which includes a user input textbox, the value of which will be used to create a table. I have been thinking about what the best way to handle invalid T-SQL table names is (though this can be extended to many other situations). Currently the only method I can think of would be to check the input string for any violations of valid table names individually, though this seems long winded and could be prone to missing certain characters for example due to my own ignorance of what is a violation and what is not.
I feel like there should be a better way of doing this but have been unable to find anything in my search so far. Can anyone help point me in the right direction?
As told you in a comment already you should not do this...
You might use something like this
USE master;
GO
CREATE DATABASE dbTest;
GO
USE dbTest;
GO
CREATE TABLE UserTables(ID INT IDENTITY CONSTRAINT PK_UserTables PRIMARY KEY
,UserInput NVARCHAR(500) NOT NULL CONSTRAINT UQ_UserInput UNIQUE);
GO
INSERT INTO UserTables VALUES(N'blah')
,(N'invalid !%$& <<& >< $')
,(N'silly 💖');
GO
SELECT * FROM UserTables;
/*
ID UserInput
1 blah
2 invalid !%$& <<& >< $
3 silly 💖
*/
GO
USE master;
GO
DROP DATABASE dbTest;
GO
You would then create your tables as Table1, Table2 and so on.
Whenever a user enters his string, you visit the table, pick the ID and create the table's name by concatenating the word Table with the ID.
There are better approaches!
But you should think of a fix schema. You will have to define columns (how many, which type, how to name them?). You will feel in hell when you have to query this. Nothing to rely on...
One approach is a classical n:m mapping
A User table (UserID, Name, ...)
A test table (TestID, TestName, TestType, ...)
The mapping table (ID, UserID, TestID, Result VARCHAR(MAX))
Depending on what you need you might add a table
question table (QuestionID, QuestionText ...)
Then use a mapping to bind questions to tests and another mapping to bind answers to such mapped questions.
another approach was to store the result as a generic container (XML or JSON). This keeps your tables slim, but needs to knwo the XML's structure in order to query it.
Many ways to skin a rabbit...
UPDATE
You ask for an explanation...
The main advantage of a relational database is the pre-known structure.
Precompiled queries, cached results, statisics, indexes demand for known structures.
Data integrity is ensured with constraints, foreign keys and so on. All this demands for known names, known types(!) and known relations.
User-specific table names, and even worse: generically defined structures, do not allow for any join, or other typical RDBMS operation. The only approach is to create each and any statement dynamically (string building)
The rule of thumb is: Whenever you think to have to create several objects of for the same, but with different names you should think about the design. It is bad to store Phone1, Phone2 and Phone3. It is better to have a side table, with a UserID and a Phone column (classical 1:n). It is bad to have SalesCustomerA, SalesCustomerB, better use a Customer table and bind its ID into a general Sales table as FK.
You see what I mean? What belongs together should live in one single table. if you need separation add columns to your table and use them for grouping and filtering.
Just imagine you want to do some statistical evaluation of all your user test tables. How would you gather the data into one big pool, if you cannot rely on some structure in common?
I hope this makes it clear...
If you still wnat to stick to your idea, you should give my code sample a try. this allows to map any silly string to a secure and easy to handle table name.
Lots can go wrong with users entering table names. A bunch of whacked out names is maintenance nightmare. A user should not even be aware of table name. It is a security risk as now the program has to have database owner authority. You want to limit users to minimum authority.
Similar to Shnugo but with composite primary key. This will prevent duplicate userID, testID.
user
ID int identity PK
varchar(100) fName
varchar(100) lName
test
ID int identity PK
varchar(100) name
userTest
int userID PK FK to User
int testID PK FK to Test
int score
select t.Name, u.Name, ut.score
from userTest ut
join Test t
on t.ID = ut.testID
join User u
on u.ID = ut.userID
order by t.Name, u.Name

Getting the Average in ROWS c#

i have a sql server database with table. These are
1stAP_TB, 2ndAP_TB, 3rdAP_TB, 4thAP_TB, 1steng_TB, 2ndeng_TB, 3rdeng_TB,
4theng_TB
all in them are in row. The numbers will be solve individually on specific column. Now, i need to know how am i going to get the average of 1stAP_TB, 2ndAP_TB, 3rdAP_TB and 4thAP_TB while there are in rows.
Also, there are multiple data that will be save inside the database. I am using C# programming language.
Try below method
create table aveexample
(a1stAP_TB int,
a2ndAP_TB int,
a3rdAP_TB int,
a4thAP_TB int,
a1steng_TB int,
a2ndeng_TB int,
a3rdeng_TB int,
a4theng_TB int
)
Sample data
insert into aveexample values(1,2,3,4,5,6,7,8)
insert into aveexample values(11,22,33,44,55,66,77,78)
insert into aveexample values(2,3,1,4,10,10,45,5)
Method 1
select *, (select AVG(totaldata)
from (values(a1stAP_TB),
(a2ndAP_TB),(a3rdAP_TB),(a4thAP_TB),(a1steng_TB),
(a2ndeng_TB),(a3rdeng_TB),(a4theng_TB)) total(totaldata))as average
from aveexample
Method 2
select ((a1stAP_TB)+
(a2ndAP_TB)+(a3rdAP_TB)+(a4thAP_TB)+(a1steng_TB)+
(a2ndeng_TB)+(a3rdeng_TB)+(a4theng_TB))/8 as Average
from aveexample
It is difficult to give concrete advice given the very limited description in the question, but from the description and comments so far, it seems to me like the database needs to be redesigned to better fit your requirements. First, you have no ID field, so there is no way to differentiate one row from the next. Then, what you are left with is a series of repeated values. The clue here is that you have "1st", "2nd", "3rd" in the column names. That's probably a sign that those columns need to be moved into rows of a related table. It may not instantly seem to be the best approach, but this is called "First Normal Form" and is a typical best practice with SQL databases. See also Database Normalization Basics.
It seems to me that what you have here is some entity (which you haven't mentioned in your question) that has a number of values associated with it. The 'entity' here should be given a unique ID and then all of the values for that entity stored with its ID.
You might have a table with the following columns:
CREATE TABLE MyItems (
ID int NOT NULL,
Sequence int NOT NULL,
Value int NOT NULL,
CONSTRAINT PK_MyValues_ID_Sequence PRIMARY KEY
(ID,Sequence)
)
Note: ID + sequence forms the unique primary key for the table and makes every row unique. This also lets you keep track of what order the items were added in. This may or may not be important to you but every table should probably have a unique primary key.
Your data table would then look something like this (the example represents two different entities, the first having 4 values and the second having 3 values):
It's difficult to show a sensible example without knowing more about the application and what it does... but with this table design you have a basis from which to add values one at a time, as you said you needed, and a way to query them back. You can use grouping to produce things like totals and averages, or you can do that in code by iterating over the results of a query or in a LINQ statement.
You can then compute the average for an entity of a given ID using a LINQ query along the lines of:
var average = MyItems.Where(p=>p.ID == 1).Average(q=>q.Value);
As an example of the flexibility of this sort of approach, you could just as easily compute the average of every second value entered across the entire database:
var averageOfSecondItems = MyItems.Where(p => p.Sequence == 2).Average(q => q.Value);
The example I've shown deals with one type of value. In your question it appears that you might have two different types of value. There are several ways you could handle that - for example you could add another column to the table if the values are always entered in pairs, or you could create a second table to hold the separate values. Again, it's hard to make a recommendation based on the limited information given.
If putting your data into First Normal Form seems like a lot of work, then your application might be a better fit for a document database ("NoSQL" database), but that is really a different question. In the question, a SQL database was specified so I've concentrated on that.

Database displaying/allowing different users certain information based on their role or ID

Im needing a system as such - they have sales, project management and service management. Each different type of employee should be able to see all information from other employees but only be able to modify the specific information for their job (example is sales cant modify the project management information but can view it). To twist this up a little more, they have multiple locations and only those locations can access data specific to that location.
Since I am most versed in C# I will be using this as the language to tie it all in together.
I have a few different ideas on how to do this and have no idea what would be the best solution to get this all done.
My current idea is for the database...
CREATE TABLE Employee
(
Employee_ID INT, --PK
Employee_Role INT, --FK
Location_ID INT --FK
);
Then, each query in the code would be specific to their Employee_Role and Location_ID. I am assuming I would manually create each user (they only have about 15 employees), assign them the values and then have the back-end code tie the queries together.
Difficulty with this is that these system queries are going to get pretty nasty (at least for me, I am not a DBA and have never done something like this).
So for example, if I wanted to display.. lets say Prospective_Clients Table for a specific location I would...and this is roughly my idea
SELECT *
FROM Prospective_Clients
INNER JOIN Employee on Prospective_Clients.Location_ID = Employee.Location_ID;
Or something to that effect.. And then to update or insert data, the query would contain in insert statements that Employee_Role has to equal whatever value I assign it. Of course them poses the questions...
Would each time an employee insert client information they have to add the location_id as well? I imagine I can just have a drop down they can select that the location name will equal the location_ID... but then there is room for error on the users parts. Or I suppose I could have it set up in the query that the clients Location_ID will equal whatever the employees Location_ID is.
Am I going about to right way doing this? Is this all needing to be controlled in the back-end code? Or is there something on the database end that is easier to do this. What I mean by this question is this making sense to anyone the way I am thinking or should I just go find a DBA to hire.
Use a separate table for permission. also make permission based on role not each employee. that's more conformable for end user and developer.
CREATE TABLE Role_Permission
(
RoleID int, -- fk
LocationID int, -- fk
CanAdd bit,
CanRead bit,
CanUpdate bit ,
CanDelete bit ,
Active bit
)

Database design for normalizing database with unique id's to build a number

Big question - I have a student number that is 10 digits long and it comes from three different database tables. For example the student number would be 0012011234 with the corresponding values:
0012 = school id
01 = class id
1234 = student id
Can someone give me an idea of how to set this database up to auto increment and build these student numbers? Right now I have three different database tables (schools, classrooms, students) and obviously they all need to be unique. I'm working in C# and am having a hard time figuring out how to auto increment the classrooms & students as the correlate to schools. Any help?
You can't (and shouldn't) use autoincrement for this. This should be done by the application that adds students. If there are multiple apps that can add students, put that logic in a separate library (or web service) that the apps can share.
This is usually called a "smart identifier" and is not what autoincrement was designed for.
To do this in the app, I would just query each table when you add a new record and choose the id as max(id) + 1 for each group (school or class).
I think your basic model is flawed.
Students can attend more than one class, and possibly more than one school. Its also quite common for schools to share facilities so a class could have students from more than one school.
You should run separate series of ids for students, schools and classes then add two relation ship tables school_x_class and student_x_class.
This should cover all eventualities.
Though this is not a good method like D Stanley said, I would say you could write a scalar-valued function to that takes the top values from these tables and combines to form your desired value
CREATE FUNCTION GetNewID
(
)
RETURNS NVarChar(15)
AS
BEGIN
-- Declare the return variable here
DECLARE #result NVarChar(15)
DECLARE #SchoolId int
,#ClassId int
,#StudentId int
SELECT TOP 1 #SchoolId=ID FROM SchoolTable
SELECT TOP 1 #StudentId=ID FROM StudentTable
SELECT TOP 1 #ClassId=ID FROM ClassTable
SET #RESULT = CONVERT(NVARCHAR,(#SchoolId+1)) + CONVERT(NVARCHAR,(#ClassId+1)) + CONVERT(NVARCHAR,(#StudentId+1))
-- Return the result of the function
RETURN #result
Then use this function dbo.GetNewID() to get the latest ID

Records for Sales Person

I am designing this database and c# app, that a record gets saved to database. now say we have three Sales Person and each should be assigned a record in strict rotation so they get to work on equal amount of records.
What I have done so far was to create one table called Records and one SalesPerson, the Records would have salesperson id as foreign key and another column that would say which agent it is assigned to and will increment this column.
Do you think this is a good design, if not can you give any ideas?
To do this I would use the analytical functions ROW_NUMBER and NTILE (assuming your RDBMS supports them). This way you can allocate each available sales person a pseudo id incrementing upwards from 1, then randomly allocate each unassigned record one of these pseudo ids to assign them equally between sales people. Using pseudo ids rather than actual ids allows for the SalesPersonID field not being continuous. e.g.
-- CREATE SOME SAMPLE DATA
DECLARE #SalesPerson TABLE (SalesPersonID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, Name VARCHAR(50) NOT NULL, Active BIT NOT NULL)
DECLARE #Record TABLE (RecordID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, SalesPersonFK INT NULL, SomeOtherInfo VARCHAR(100))
INSERT #SalesPerson VALUES ('TEST1', 1), ('TEST2', 0), ('TEST3', 1), ('TEST4', 1);
INSERT #Record (SomeOtherInfo)
SELECT Name
FROM Sys.all_Objects
With this sample data the first step is to find the number of available sales people to allocate records to:
DECLARE #Count INT = (SELECT COUNT(*) FROM #SalesPerson WHERE Active = 1)
Next using CTEs to contain the window functions (as they can't be used in join clauses)
;WITH Records AS
( SELECT *,
NTILE(#Count) OVER(ORDER BY NEWID()) [PseudoSalesPersonID]
FROM #Record
WHERE SalesPersonFK IS NULL -- UNALLOCATED RECORDS
), SalesPeople AS
( SELECT SalesPersonID,
ROW_NUMBER() OVER (ORDER BY SalesPersonID) [RowNumber]
FROM #SalesPerson
WHERE Active = 1 -- ACTIVE SALES PEOPLE
)
Finally update the records CTE with the actual sales personID rather than a pseudo id
UPDATE Records
SET SalesPersonFK = SalesPeople.SalesPersonID
FROM Records
INNER JOIN SalesPeople
ON PseudoSalesPersonID = RowNumber
ALL COMBINED IN AN SQL FIDDLE
This is quite confusing as I suspect you're using the database term 'record' aswell as an object/entity 'Record'.
The simple concept of having a unique identifier in one table that also features as a foreign key in another table is fine though, yes. It avoids redundancy.
Basics of normalisation
Its mostly as DeeMac said. But if your Record is an object (i.e. it has all the work details or its a sale or a transaction) then you need to separate that table. Have a table Record with all the details to that particular object. Have another table `Salesman' with all the details about the Sales Person. (In a good design, you would only add particular business related attributes of the position in this table. All the personal detail will go in a different table)
Now for your problem, you can build two separate tables. One would be Record_Assignment where you will assign a Record to a Salesman. This table will hold all the active jobs. Another table will be Archived_Record_Assignment which will hold all the past jobs. You move all the completed jobs here.
For equal assignment of work, you said you want circular assignment. I am not sure if you want to spread work amongst all sales person available or only certain number. Usually assignments are given by team. Create a table (say table SalesTeam)with the Salesman ids of the sales persons you want to assign the jobs (add team id, if you have multiple teams working on their own assigned work areas or customers. That's usually the case). When you want to assign new job, query the Record_Assignment table for last record, get the Salesman id and assign the job to the next salesman in the SalesTeam table. The assignment will be done through business logic (coding).
I am not fully aware of your scenario. These are all my speculations so if you see something off according to your scenario, let me know.
Good Luck!

Categories

Resources