I'm having a column "SelectedCustomers" in the database table holding selected customers that manager is editing there details NOW. For example it can contain this string '111,222,333' which means that at this current time the manager is selected to edit customers with these ids 111,222 and 333. If another manager is trying to edit a customer that already editing by the first manager the second manager should get an error, preventing him from editing this customer. This check should hold at the sql. And lets say that checking store procedure getting a nvarchard "CheckCustomers" with this value : '234,222,341'. And because the second manager is trying to edit customer with id 222 that is already selected for editing by the first manager. The second manager will get an error message. How should the sql query should be? (I have already a "split" function.)
SELECT * FROM dbo.test WHERE dbo.Split(SelectedCustomers)IN (CheckCustomers)
Why not simply add another column 'IsBeingEdited' in the customer's table? You can then simply:
SELECT id FROM customer WHERE id IN (CheckCustomers) AND IsBeingEdited = 1;
The list shows id's of customer currently being edited, right (and easily changable to other column like name)? So you can also shows that to the 2nd manager.
You can do this with CROSS APPLY.
IF EXISTS (
SELECT S.CustomerID
FROM dbo.test T
CROSS APPLY dbo.Split(T.SelectedCustomers) S
CROSS APPLY dbo.Split(T.CheckCustomers) C
WHERE S.CustomerID = C.CustomerID
)
BEGIN
RAISERROR('Customer is locked by another user', 16, 0)
END
I suggest that this design be changed. Any time do something like splitting strings to get at the 'hidden' fields inside the string is necessary it's a violation of one of the basic principles of using a relational database, which is that each field in a row should store a single value. Can this be made to work? Probably. Is it a good idea to do so? Not in my opinion.
As others have pointed out there are several ways to change the design. A column could be added to the Customer table to indicate that the customer is locked. A separate table (CustomerLocked, for example) containing the CustomerID and the manager's ID could be used - this would allow additional information to be added such as the time that the customer was locked, etc, which might be useful if someone were to lock a customer and then walk away from their desk.
I believe that either of these changes would satisfy the requirements. For example, let's say that a LockedCustomer table is created:
Table LockedCustomer
CustomerID NUMBER PRIMARY KEY
ManagerID VARCHAR
AddDate DATE
and let's say that manager A has locked customers 111, 222, and 333; thus in the LockedCustomer table the following rows would exist:
CustomerID ManagerID
111 A
222 A
333 A
Now along comes manager B, who wishes to lock customer 222. The application manager B is using attempts to insert a new row into the LockedCustomer table, as follows:
INSERT INTO LockedCustomer (CustomerID, ManagerID)
VALUES (222, 'B');
This statement should fail because CustomerID 222 already exists in the table, and the CustomerID column is the primary key on LockedCustomer. This makes sense as we only want a given CustomerID to exist at most one time in the LockedCustomer table at any point in time. The application manager B is using could then detect that the INSERT failed due to a primary key constraint violation, and would understand that this meant that the customer could not be locked at this time. By re-querying the LockedCustomer table for additional data, as in:
SELECT *
FROM LockedCustomer
WHERE CustomerID = 222
the application could present a dialog to Manager B that might look something like
The customer you wished to edit (222) is currently in use
by Manager A since 03-Jun-2011 2:17 PM. Would you like to
A) Wait
B) Send an email to Manager A
C) Take a long vacation
D) Violate company policy regarding alcohol consumption
during working hours
Please select one of the above options?
Share and enjoy.
Related
I have a database with an "inventory" table which contains products from a shop. Each product is identified using an ID number set in the "ID" column of the table.
I want to be able to delete a product from the table, but keeping the deleted product's id number for future product insertions into the database.
As a demonstration I inserted 4 items and named all of them "test"
And just as an example I named the "deleted" product as "vacio" (empty in spanish) to show the one that i deleted.
Now, if want to add another product in the future, the id number 2 is unused and I want to add the product with that id number instead of 4 (following the given example).
The DELETE query is no good since it erases the id number as well so its a no go.
I thought about checking for the first row of the table that contains the value "vacio" and using the UPDATE query in all fields except id but this doesnt feel "classy" and is not very efficient as It should have to update values a lot of times.
Is there some nice way of doing this?
I would not actually recommend reusing old identifiers. For one, this prevents you from using the auto_increment feature, which mean that you need to manually handle the column for each and every insertion: this adds complexity and is not efficient. Also, it might cause integrity issues in your database if you have other tables referencing the product id.
But if you really want to go that way: I would go for the deletion option. If there are foreign keys referencing the column, make sure that they have the on delete cascade option enabled so data is properly purged from dependent tables when a product is dropped.
Then, you can fill the first available gap the next time your create a new product with the following query:
insert into products(id, categoria, producto)
select min(id) + 1, 'my new category', 'my new product'
from products p
where not exists (select 1 from products p1 where p1.id = p.id + 1)
You could have a new column ESTADO where you handle if a record is active (1) or inactive (0). Then, to obtain only "undeleted" records you just have to filter by the new column. That way, you also prevent changing the product name to "vacio", which might be useful in the future.
Not sure if this is the right way to phrase the question but I have a database shown below
ProductA ProgramA
ProductB ProgramB
ProductC ProgramBoth
One issue I'm facing is when I put this into a dashboard and I use the dashboard to filter only ProgramA, I want to see both Product A and Product C. And when I filter ProgramB, I would like to see both Product B and Product C. Technically the user can select two of the programs in the dashboard drop down ("ProgramA + ProgramBoth), but they don't.
Am I pushing the limits of SQL? Is there a way around? As a note, I'm importing this from a Google Sheet, so I can change the underlying values if that's easier. In Google Sheets, I have a dropdown so only one value can be put in at a time (can be changed).
You are not pushing the limits of SQL :-)
What you are describing is a many-to-many relationship, and instead of thinking about a relationship of "both", think about it as a row for each relationship so for product C you will have 2 rows - one for program A and one for program B, something along the lines of:
CREATE TABLE Product_Programs
(
Product VARCHAR(10) NOT NULL REFERENCES Products(Product),
Program VARCHAR(10) NOT NULL REFERENCES Programs(Program),
PRIMARY KEY (Product, Program)
);
INSERT INTO Product_Programs (Product, Program)
VALUES ('ProductA', 'ProgramA'),
('ProductB', 'ProgramB'),
('ProductC', 'ProgramA'),
('ProductC', 'ProgramB');
Now you can easily query for any product participating in a program with
SELECT Product
FROM Product_Programs
WHERE Program = 'ProgramA';
Which will return both product A and product C.
HTH
I'm having some trouble trying to figure out how to Update a singular cell in a record using SQLPLUS. In a table called CUSTOMER I, the CUSTOMER_NUMBER column is not a Primary Key and is also a null value. I mistakenly made two of the CUSTOMER NUMBER values the same.
My question now, is there any way to ALTER and UPDATE a singular cell in a record? I need to change the Adams row to 412 instead of 522.
This is why primary keys are important!
You have to be VERY CAREFUL to update on another (hopefully unique) field, or you might update more records than expected. Since Adams, Sally appears twice in your example, the street name does differ though for the 522 entry that you mention.
This assumes, of course, that there is only ONE address with this name!
(and that there is not another "16 ELM" in the table that might not appear in your example output)
Try: UPDATE CUSTOMER set CUS=412 WHERE STREET='16 ELM'
If there are other entries having the same address, then those would also get updated. If you had instead used WHERE LAST='Adams', then you would have updated 2 records.
update customer
set cus = 412
where rowid not in (select max(rowid) from customers group by cus);
This should serve your purpose. Please provide a feedback on the outcome.
I don't know what is the best wording of the question, but I have a table that has 2 columns: ID and NAME.
when I delete a record from the table the related ID field deleted with it and then the sequence spoils.
take this example:
if I deleted row number 2, the sequence of ID column will be: 1,3,4
How to make it: 1,2,3
ID's are meant to be unique for a reason. Consider this scenario:
**Customers**
id value
1 John
2 Jackie
**Accounts**
id customer_id balance
1 1 $500
2 2 $1000
In the case of a relational database, say you were to delete "John" from the database. Now Jackie would take on the customer_id of 1. When Jackie goes in to check here balance, she will now show $500 short.
Granted, you could go through and update all of her other records, but A) this would be a massive pain in the ass. B) It would be very easy to make mistakes, especially in a large database.
Ids (primary keys in this case) are meant to be the rock that holds your relational database together, and you should always be able to rely on that value regardless of the table.
As JohnFx pointed out, should you want a value that shows the order of the user, consider using a built in function when querying.
In SQL Server identity columns are not guaranteed to be sequential. You can use the ROW_NUMBER function to generate a sequential list of ids when you query the data from the database:
SELECT
ROW_NUMBER() OVER (ORDER BY Id) AS SequentialId,
Id As UniqueId,
Name
FROM dbo.Details
If you want sequential numbers don't store them in the database. That is just a maintenance nightmare, and I really can't think of a very good reason you'd even want to bother.
Just generate them dynamically using tSQL's RowNumber function when you query the data.
The whole point of an Identity column is creating a reliable identifier that you can count on pointing to that row in the DB. If you shift them around you undermine the main reason you WANT an ID.
In a real world example, how would you feel if the IRS wanted to change your SSN every week so they could keep the Social Security Numbers sequential after people died off?
SQL Server 2008 Database Question.
I have 2 tables, for arguments sake called Customers and Users where a single Customer can have 1 to n Users. The Customers table generates a CustomerId which is a seeded identity with a +1 increment on it. What I'm after in the Users table is a compound key comprising the CustomerId and a sequence number such that in all cases, the first user has a sequence of 1 and subsequent users are added at x+1.
So the table looks like this...
CustomerId (PK, FK)
UserId (PK)
Name
...and if for example, Customer 485 had three customers the data would look like...
CustomerId | UserId | Name
----------
485 | 1 | John
485 | 2 | Mark
485 | 3 | Luke
I appreciate that I can manually add the 1,2,3,...,n entry for UserId however I would like to get this to happen automatically on row insert in SQL, so that in the example shown I could effectively insert rows with the CustomerId and the Name with SQL Server protecting the Identity etc. Is there a way to do this through the database design itself - when I set UserId as an identity it runs 1 to infinity across all customers which isn't what I am looking for - have I got a setting wrong somewhere, or is this not an option?
Hope that makes sense - thanks for your help
I can think of no automatic way to do this without implementing a custom Stored Procedure that inserted the rows and checked to increment the Id appropriately, althouh others with more knowledge may have a better idea.
However, this smells to me of naturalising a surrogate key - which is not always a good idea.
More info here:
http://www.agiledata.org/essays/keys.html
That's not really an option with a regular identity column, but you could set up an insert trigger to auto populate the user id though.
The naive way to do this would be to have the trigger select the max user id from the users table for the customer id on the inserted record, then add one to that. However, you'll run into concurrency problems there if more than one person is creating a user record at the same time.
A better solution would be to have a NextUserID column on the customers table. In your trigger you would:
Start a transaction.
Increment the NextUserID for the customer (locking the row).
Select the updated next user id.
use that for the new User record.
commit the transaction.
This should ensure that simultaneous additions of users don't result in the same user id being used more than once.
All that said, I would recommend that you just don't do it. It's more trouble than it's worth and just smells like a bad idea to begin with.
So you want a generated user_id field that increments within the confines of a customer_id.
I can't think of one database where that concept exists.
You could implement it with a trigger. But my question is: WHY?
Surrogate keys are supposed to not have any kind of meaning. Why would you try to make a key that, simultaneously, is the surrogate and implies order?
My suggestions:
Create a date_created field, defaulting to getDate(). That will allow you to know the order (time based) in which each user_id was created.
Create an ordinal field - which can be updated by a trigger, to support that order.
Hope that helps.