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!
Related
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.
I am new to SQL and today I got assigned an important task - to create a migration script for data in a table. From my understanding, a migration script is copying data from table A and move it to other tables B and C and so on. This seems to be frequent when database designs change constantly and the team wants to preserve data.
My task:
I have a JobOffer table, with the CityId field. Now the team wants to delete that field, and to preserve information they will add the CityId to the Address table and connect both tables using an intermidiary table called Location (this allows a JobOffer to have several Addresses).
I have no idea on how to perform this task. An analogy in c# of what I prentend is this:
foreach (var row in JobOffer)
{
int addressId;
if (!Address.Contains(row.CityId)){
addressId = Address.add(row.CityId);
Locaion.add(row.JobOfferId, addressId);
}
else
{
Locaion.add(row.JobOfferId, Address.get(row.CityId));
}
}
How do I do it in SQL?
You need three tables - one for the candidates, one for the addresses (locations) and one that links the two. The third table is necessary because what you described is a many to many relationship. A single candidate may have multiple locations and a single location may house multiple candidates.
When I created similar to yours it took two scans of the input data:
The first checked if I had all the locations. If any were missing I inserted it into the location table.
The second scan inserted data into the candidate and canditatelocs table. At this point I knew for sure that I had an address for every candidate in the locations table.
Here is a description of the tables:
create table candidate (candidateid int identity primary key, idate datetime default getdate(), name varchar(200))
create table candidatelocs (candidateid int, locid int)
create table locations ( locid int identity primary key, city varchar(500), state varchar(500))
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
I have two tables in MySQL, let's say:
Product
id (unique)
name
Purchases
id (unique)
fk_productid (references Product.id)
buyerName
I am using InnoDB and created a foreign key from purchases.fk_productid to product.id.
Assume we have no products in the table so far and no purchases. Now someone purchases the product with id 10 which will cause a failure if I simply try this query:
INSERT INTO purchases (fk_productid, buyerName) VALUES (10, "Andreas")
What can I do to insert the purchase anyway? I can think of two things:
Add NULL as fk_productid, but how do I do that directly in the query if it fails?
Add a dumy entry in the product table, but how would I do that? How can I automatically add
INSERT INTO products (id, name) VALUES (10, "???")
before the other query?
Should this be done with triggers, procedures or is there even an easier way I don't know?
Well, first I would say you're probably safer to refuse the order and tell the user to come back, because you should be doing things like checking stock, price, etc. in your product table before verifying a purchase.
Anyway, this should solve your immediate problem:
INSERT IGNORE INTO products (id, name, isDummy) VALUES (10, "TBD", 1); // does nothing if product id 10 already exists
INSERT INTO purchases (fk_productid, buyerName) VALUES (10, "Andreas");
I decided to go with this solution:
INSERT INTO purchases (fk_productid, buyerName) VALUES ((SELECT id FROM product WHERE id=10 LIMIT 1), "Andreas");
I agree that in production this case should never happen - don't worry, it won't. This was more of a simple description of a larger problem I had. ;-)
Thanks for your ideas before!
I am receiving a large list of current account numbers daily, and storing them in a database. My task is to find added and released accounts from each file. Right now, I have 4 SQL tables, (AccountsCurrent, AccountsNew, AccountsAdded, AccountsRemoved). When I receive a file, I am adding it entirely to AccountsNew. Then running the below queries to find which we added and removed.
INSERT AccountsAdded(AccountNum, Name) SELECT AccountNum, Name FROM AccountsNew WHERE AccountNumber not in (SELECT AccountNum FROM AccountsCurrent)
INSERT AccountsRemoved(AccountNum, Name) SELECT AccountNum, Name FROM AccountsCurrent WHERE AccountNumber not in (SELECT AccountNum FROM AccountsNew)
TRUNCATE TABLE AccountsCurrent
INSERT AccountsCurrent(AccountNum, Name) SELECT AccountNum, Name FROM AccountsNew
TRUNCATE TABLE AccountsNew
Right now, I am differencing about 250,000 accounts, but this is going to keep growing. Is this the best method, do you have any other ideas?
EDIT:
This is an MSSQL 2000 database. I'm using c# to process the file.
The only data I am focused on is the accounts that were added and removed between the last and current files. The AccountsCurrent, is only used to determine what accounts were added or removed.
To be honest, I think that I'd follow something like your approach. One thing is that you could remove the truncate, do a rename of the "new" to "current" and re-create "new".
Sounds like a history/audit process that might be better done using triggers. Have a separate history table that captures changes (e.g., timestamp, operation, who performed the change, etc.)
New and deleted accounts are easy to understand. "Current" accounts implies that there's an intermediate state between being new and deleted. I don't see any difference between "new" and "added".
I wouldn't have four tables. I'd have a STATUS table that would have the different possible states, and ACCOUNTS or the HISTORY table would have a foreign key to it.
Using IN clauses on long lists can be slow.
If the tables are indexed, using a LEFT JOIN can prove to be faster...
INSERT INTO [table] (
[fields]
)
SELECT
[fields]
FROM
[table1]
LEFT JOIN
[table2]
ON [join condition]
WHERE
[table2].[id] IS NULL
This assumes 1:1 relationships and not 1:many. If you have 1:many you can do any of...
1. SELECT DISTINCT
2. Use a GROUP BY clause
3. Use a different query, see below...
INSERT INTO [table] (
[fields]
)
SELECT
[fields]
FROM
[table1]
WHERE
EXISTS (SELECT * FROM [table2] WHERE [condition to match tables 1 and 2])
-- # This is quick provided that all fields to match the two tables are
-- # indexed in both tables. Should then be much faster than the IN clause.
You could also subtract the intersection to get the differences in one table.
If the initial file is ordered in a sensible and consistent way (big IF!), it would run considerably faster as a C# program which logically compared the files.