BuyerAccounts and SellerAccounts Tables that both reference Accounts Table. Create Account INSERT - sql-server

I am designing tables to store accounts of website users. There will be two types of accounts for the website, BuyerAccounts and SellerAccounts. My approach is to have a master Accounts table that will store information that is common to both types of accounts and then to have a table each for BuyerAccounts and SellerAccounts.
This is the basic setup..
create table Accounts
(
UserID_PK int identity not null primary key,
Email varchar(50) not null unique,
UserPassword varchar(50) not null,
Salt varchar(50) not null,
AccountType tinyint not null,
);
create table SellerAccounts
(
SellerID_PK int not null primary key foreign key references Accounts(UserID_PK),
SellerColumn int
);
create table BuyerAccounts
(
BuyerID_PK int not null primary key foreign key references Accounts(UserID_PK),
DeliveryAddress string,
BuyerColumn string
);
Each of SellerAccounts and BuyerAccounts has a Primary Key that is also a Foreign Key referencing the Primary Key of the Accounts Table.
This all seems okay so far to me but I'm a beginner so please say if I have done something wrong or bad so far.
When an account is created I want to create a record in Accounts and also a record in either BuyerAccounts or SellerAccounts.
How do I do this? I'm going to make it a stored procedure but my primary concern here is how to make an INSERT into two tables that are linked. Say a BuyerAccount is being created. Does account creation have to be broken into two INSERTS? First into Accounts, so that the UserID_PK for the account that will also be the BuyerID_PK of BuyerAccounts is known and then the rest of the account info can be inserted into BuyerAccounts? Is this how is should work?
How do I write an INSERT that returns UserID_PK so that I have it for the second insert? Or even better can SQL Server do something clever and just work out the Values of UserID_PK and BuyerID_PK for me and do everything with just one insert?
Thank you for your help!

use scope_identity()
example
declare #id int
insert Accounts values('a#b.com','pass','bla',1)
select #id = SCOPE_IDENTITY()
insert SellerAccounts values(#id,1)
select * from SellerAccounts

Related

Did I write this query correctly?

I'm given the question
My solution is
USE Finances
CREATE TABLE Account
(AccountID varchar(25) NOT NULL PRIMARY KEY,
AccountName varchar(50) NOT NULL,
AccountAddress varchar(30) NULL,
AccountCity varchar(25) NULL,
AccountState char(2) NULL,
AccountZip varchar(10) NULL,
AccountPhone varchar(14) NULL)
CREATE TABLE Transactions
(TransactionID INT NOT NULL PRIMARY KEY IDENTITY,
AccountID varchar(25) REFERENCES Account (AccountID),
TransactionDate smalldatetime NOT NULL,
TransactionAmount money NOT NULL)
CREATE TABLE Register
(RegisterID INT NOT NULL PRIMARY KEY IDENTITY,
TransactionID INT NOT NULL REFERENCES Transactions (TransactionID));
Part of the reason I'm confused is because I don't completely understand the primary/foreign key relationship. Like I look at the question and say, ok the registerID will be the single/unique ID to seperate every account from each other. But I see that there's a TranasctionID in the same table. And that TransactionID is a foreign key to the TransactionID in the transaction table. So does that mean whenever a new tranasctionID is added there is a corresponding RegisterID. Like whats the point of the Register ID?? And did I write this query correctly?
The only issue in you script is your column "AccountID" in table "Transactions" is creating with allowing NULL and "TransactionAmount" is NOT NULL where as it should allow NULL. Other wise I found the tables are creating with appropriate type and relation as instructed.
Regarding your confusion, I am confused too thinking about the purpose of table "Register". As per instruction, yes it will create a new row every time a new transaction take place. As registerId is a PK, with each transaction a new PK will generate. But the question is why a new RegisterId is required for each TransactionID? If RegisterId is referring to other table for a transaction details, it can be easily use the TransactionID directly as both TransactionID and RegisterId are unique (PK).

how to add this condition in table?

i want to create a table which store USERNAME and DOMAIN(both are col. name of same table).
One User can only belong to one domain.
and the The Domain values can be stored in the another table, lets call it "LIST".
Set in your users table domain_id (as relation with domain), then use constraints for two columns (username and domain_id).
MySql example:
ALTER TABLE users ADD UNIQUE unique_index (username, domain_id);
P.S. Look for analog in SQL server. (see example in comments)
I suggest this kind of script to create your tables and put on them determined conditions:
--Create table that will store domains
CREATE TABLE DICT_DOMAINS (
DomainID int IDENTITY(1,1),
DomainName nvarchar(255),
CONSTRAINT [PK_DICT_DOMAINS] PRIMARY KEY CLUSTERED (DomainID ASC)
)
--DomainNames must be unique so we add non clustered index
CREATE UNIQUE NONCLUSTERED INDEX [UX_DICT_DOMAINS_DomainName] ON DICT_DOMAINS (DomainName ASC)
--Create table to store users and there domains
CREATE TABLE USERS_DOMAINS (
DomainID int,
UserName nvarchar(255)
)
--Add foreign key to DICT_DOMAINS table
ALTER TABLE USERS_DOMAINS WITH CHECK ADD CONSTRAINT [FK_USERS_DOMAINS_DICT_DOMAINS] FOREIGN KEY (DomainID)
REFERENCES DICT_DOMAINS (DomainID)
--Usernames must be uniq, no need to add DomainsId to this index
--This is sufficient to provide condition that
--One User can only belong to one domain.
CREATE UNIQUE NONCLUSTERED INDEX [UX_USERS_DOMAINS_UserName] ON USERS_DOMAINS (UserName ASC)

Load user extract with stored procedure

I have a ASP MVC web application that is required to load a user extract each day from a file. The users in the database should be updated accordingly: deleted if not in source, updated if in source and target, and created if only in source. While doing this, certain rights should also automatically be given to the users. If there is any error, nothing should happen at all.
First I tried to do this with Entity Framework, but the SaveChanges call takes around two minutes to return, which is a lot for the relatively small amount of users (~140 000).
My idea now is to write a stored procedure that would do the updating. I would pass the list of new users as a parameter. The type of my temporary table:
CREATE TYPE [dbo].[TempUserType] AS TABLE
(
[Uid] NVARCHAR(80) NOT NULL PRIMARY KEY,
[GivenName] NVARCHAR(80) NOT NULL,
[FamilyName] NVARCHAR(80) NOT NULL,
[Email] NVARCHAR(256) NOT NULL,
[GiveRight1] BIT NOT NULL,
[GiveRight2] BIT NOT NULL,
[GiveRight3] BIT NOT NULL
)
The users:
CREATE TABLE [dbo].[User] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Uid] NVARCHAR (80) NOT NULL,
[GivenName] NVARCHAR (80) NOT NULL,
[FamilyName] NVARCHAR (80) NOT NULL,
[Email] NVARCHAR (256) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC),
UNIQUE NONCLUSTERED ([Uid] ASC)
);
The user roles:
CREATE TABLE [dbo].[UserRole] (
[UserId] INT NOT NULL,
[RoleId] INT NOT NULL,
CONSTRAINT [PK_UserRole] PRIMARY KEY CLUSTERED ([UserId] ASC, [RoleId] ASC),
CONSTRAINT [FK_UserRole_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]),
CONSTRAINT [FK_UserRole_Role] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[Role] ([Id])
);
The procedure I am stuck writing:
CREATE PROCEDURE [dbo].[UpdateUsers]
#extractedUsers TempUserType READONLY
AS
BEGIN TRANSACTION
MERGE
[dbo].[User] AS trg
USING
#extractedUsers AS src
ON
(trg.[Uid] = src.[Uid])
WHEN MATCHED THEN
UPDATE SET
trg.GivenName = src.GivenName,
trg.FamilyName = src.FamilyName,
trg.Email = src.Email
WHEN NOT MATCHED BY TARGET THEN
INSERT
([Uid], GivenName, FamilyName, Email)
VALUES
(src.[Uid], src.GivenName, src.FamilyName, src.Email)
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
COMMIT TRANSACTION
My question: is the use of a procedure with merge appropriate in this case to achieve the performance improvement over EF? How can I attribute roles according to the 3 boolean values that are in the source table?
Roles can be hadcoded, meaning I know that the Right1 corresponds to the RoleId 1, Right 2 to RoleId 2 and Right 3 to RoleId3.
After reading the question I want to leave an idea for the solution.
For me it makes more sense to have an IF / ELSE for calling the update or the insert, not using the merge since you need the UserId that you are updating/inserting to add it's role permissions.
You can check if UId exists and if so update the user details, if does not exists then create and keep the new Identity value.
In both cases, when having the user ID add the corresponding permissions according to the boolean values with IF statements.
Pseudo-code:
If UserId exists in UsersTable
Update
Store UserId
Else
Insert
Store created UserId (using the ##IDENTITY)
If bool1 add permission 1
If bool3 add permission 2
If bool3 add permission 3

Normalize 3 database tables

Hello I have a problem seperating my 3 payment types: CASH, CREDIT, BANK
Each of them has different details.
The details are user defined which means that in a credit card payment (for ex: you should input your credit card details, bank details, cash details (currency and etc))
Business Process: The user will choose his payment type in a
combobox:
Then the user will input the details of that payment type.
This is what I've tried:
PaymentType(PaymentType_ID(PK), PaymentTypes)
...
.....
......
.........
then I'm stuck. I don't know how. Please help me. If you will answer explain to me please. I don't want to ask the same question here again. If I'm faced with a similar situation.
***I can't merge all of them into 1 table because they different columns. They have different specific details...
All three payment types have a few things in common. They all have an account number, an amount, a timestamp, a payment type, and some kind of transaction identifier. All the common attributes go in one table. (Some of the data types are deliberately naive, because they're application-dependent, and I don't know your application.)
create table payment_types (
payment_type_code char(2) primary key,
payment_type varchar(8) not null unique
);
insert into payment_types values
('Ca', 'Cash'),('Cr', 'Credit'),('Ba', 'Bank');
create table payments (
transaction_id integer primary key,
account_code varchar(5) not null, -- references accounts, not shown
amount_usd numeric(5,2) not null,
payment_type_code char(2) not null references payment_types (payment_type_code),
transaction_timestamp timestamp not null default current_timestamp,
unique (transaction_id, payment_type_code)
);
The unique constraint on {transaction_id, payment_type_code} lets SQL use that pair of columns as the target for a foreign key constraint. That's crucial to keeping the rows from the several tables from getting mixed up.
Each payment has different attributes, depending on the payment type. And each payment can be of only one type.
create table payment_cash (
transaction_id integer primary key,
payment_type_code char(2) not null default 'Ca' check (payment_type_code = 'Ca'),
foreign key (transaction_id, payment_type_code)
references payments (transaction_id, payment_type_code),
other_cash_columns char(1) not null
);
create table payment_credit (
transaction_id integer primary key,
payment_type_code char(2) not null default 'Cr' check (payment_type_code = 'Cr'),
foreign key (transaction_id, payment_type_code)
references payments (transaction_id, payment_type_code),
other_credit_columns char(1) not null
);
create table payment_bank (
transaction_id integer primary key,
payment_type_code char(2) not null default 'Ba' check (payment_type_code = 'Ba'),
foreign key (transaction_id, payment_type_code)
references payments (transaction_id, payment_type_code),
other_bank_columns char(1) not null
);
The default value and check constraint for payment_type_code makes it impossible, for example, to insert credit details for a cash payment. That would be possible--and it would be a Bad Thing--if the foreign key constraint used only the transaction id.
As a general rule, you don't cascade updates or deletes for financial transactions. Instead, correct errors by inserting a compensating transaction.
To make this more friendly to users and application code, create three updatable views that join the payments table to the detail. How to make them updatable depends on your dbms.
create view credit_payments_all as
select p.transaction_id, p.account_code, p.amount_usd,
p.payment_type_code, p.transaction_timestamp,
c.other_credit_columns
from payments p
inner join payment_credit c on c.transaction_id = p.transaction_id
-- Rules, triggers, stored procedures, functions, or whatever you need
-- to make this view updatable.
Then any code that needs to insert a credit transaction can just insert into the view credit_payments_all.

Foreign Key to multiple tables

I've got 3 relevant tables in my database.
CREATE TABLE dbo.Group
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.User
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.Ticket
(
ID int NOT NULL,
Owner int NOT NULL,
Subject varchar(50) NULL
)
Users belong to multiple groups. This is done via a many to many relationship, but irrelevant in this case. A ticket can be owned by either a group or a user, via the dbo.Ticket.Owner field.
What would be the MOST CORRECT way describe this relationship between a ticket and optionally a user or a group?
I'm thinking that I should add a flag in the ticket table that says what type owns it.
You have a few options, all varying in "correctness" and ease of use. As always, the right design depends on your needs.
You could simply create two columns in Ticket, OwnedByUserId and OwnedByGroupId, and have nullable Foreign Keys to each table.
You could create M:M reference tables enabling both ticket:user and ticket:group relationships. Perhaps in future you will want to allow a single ticket to be owned by multiple users or groups? This design does not enforce that a ticket must be owned by a single entity only.
You could create a default group for every user and have tickets simply owned by either a true Group or a User's default Group.
Or (my choice) model an entity that acts as a base for both Users and Groups, and have tickets owned by that entity.
Heres a rough example using your posted schema:
create table dbo.PartyType
(
PartyTypeId tinyint primary key,
PartyTypeName varchar(10)
)
insert into dbo.PartyType
values(1, 'User'), (2, 'Group');
create table dbo.Party
(
PartyId int identity(1,1) primary key,
PartyTypeId tinyint references dbo.PartyType(PartyTypeId),
unique (PartyId, PartyTypeId)
)
CREATE TABLE dbo.[Group]
(
ID int primary key,
Name varchar(50) NOT NULL,
PartyTypeId as cast(2 as tinyint) persisted,
foreign key (ID, PartyTypeId) references Party(PartyId, PartyTypeID)
)
CREATE TABLE dbo.[User]
(
ID int primary key,
Name varchar(50) NOT NULL,
PartyTypeId as cast(1 as tinyint) persisted,
foreign key (ID, PartyTypeId) references Party(PartyID, PartyTypeID)
)
CREATE TABLE dbo.Ticket
(
ID int primary key,
[Owner] int NOT NULL references dbo.Party(PartyId),
[Subject] varchar(50) NULL
)
The first option in #Nathan Skerl's list is what was implemented in a project I once worked with, where a similar relationship was established between three tables. (One of them referenced two others, one at a time.)
So, the referencing table had two foreign key columns, and also it had a constraint to guarantee that exactly one table (not both, not neither) was referenced by a single row.
Here's how it could look when applied to your tables:
CREATE TABLE dbo.[Group]
(
ID int NOT NULL CONSTRAINT PK_Group PRIMARY KEY,
Name varchar(50) NOT NULL
);
CREATE TABLE dbo.[User]
(
ID int NOT NULL CONSTRAINT PK_User PRIMARY KEY,
Name varchar(50) NOT NULL
);
CREATE TABLE dbo.Ticket
(
ID int NOT NULL CONSTRAINT PK_Ticket PRIMARY KEY,
OwnerGroup int NULL
CONSTRAINT FK_Ticket_Group FOREIGN KEY REFERENCES dbo.[Group] (ID),
OwnerUser int NULL
CONSTRAINT FK_Ticket_User FOREIGN KEY REFERENCES dbo.[User] (ID),
Subject varchar(50) NULL,
CONSTRAINT CK_Ticket_GroupUser CHECK (
CASE WHEN OwnerGroup IS NULL THEN 0 ELSE 1 END +
CASE WHEN OwnerUser IS NULL THEN 0 ELSE 1 END = 1
)
);
As you can see, the Ticket table has two columns, OwnerGroup and OwnerUser, both of which are nullable foreign keys. (The respective columns in the other two tables are made primary keys accordingly.) The CK_Ticket_GroupUser check constraint ensures that only one of the two foreign key columns contains a reference (the other being NULL, that's why both have to be nullable).
(The primary key on Ticket.ID is not necessary for this particular implementation, but it definitely wouldn't harm to have one in a table like this.)
Another approach is to create an association table that contains columns for each potential resource type. In your example, each of the two existing owner types has their own table (which means you have something to reference). If this will always be the case you can have something like this:
CREATE TABLE dbo.Group
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.User
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.Ticket
(
ID int NOT NULL,
Owner_ID int NOT NULL,
Subject varchar(50) NULL
)
CREATE TABLE dbo.Owner
(
ID int NOT NULL,
User_ID int NULL,
Group_ID int NULL,
{{AdditionalEntity_ID}} int NOT NULL
)
With this solution, you would continue to add new columns as you add new entities to the database and you would delete and recreate the foreign key constraint pattern shown by #Nathan Skerl. This solution is very similar to #Nathan Skerl but looks different (up to preference).
If you are not going to have a new Table for each new Owner type then maybe it would be good to include an owner_type instead of a foreign key column for each potential Owner:
CREATE TABLE dbo.Group
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.User
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.Ticket
(
ID int NOT NULL,
Owner_ID int NOT NULL,
Owner_Type string NOT NULL, -- In our example, this would be "User" or "Group"
Subject varchar(50) NULL
)
With the above method, you could add as many Owner Types as you want. Owner_ID would not have a foreign key constraint but would be used as a reference to the other tables. The downside is that you would have to look at the table to see what the owner types there are since it isn't immediately obvious based upon the schema. I would only suggest this if you don't know the owner types beforehand and they won't be linking to other tables. If you do know the owner types beforehand, I would go with a solution like #Nathan Skerl.
Sorry if I got some SQL wrong, I just threw this together.
Yet another option is to have, in Ticket, one column specifying the owning entity type (User or Group), second column with referenced User or Group id and NOT to use Foreign Keys but instead rely on a Trigger to enforce referential integrity.
Two advantages I see here over Nathan's excellent model (above):
More immediate clarity and simplicity.
Simpler queries to write.
you can also use an enum to identify whether Owner is user or group like this:
CREATE TABLE dbo.Group
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.User
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TYPE Enum_OwnerType AS ENUM ('Group', 'User');
CREATE TABLE dbo.Ticket
(
ID int NOT NULL,
Owner int NOT NULL,
OwnerType Enum_OwnerType NOT NULL,
Subject varchar(50) NULL
)
Maybe it's no better than any of proposed solutions, it might not offer any advantage. In fact, I think that this might require altering Enum_OwnerType and even ticket in order to change OwnerType, I guess... I hope it's useful anyway.
I have many cases like this and I just use polymorphic ability like below:
example
I have turnovers table that have this columns id, amount, user_id and I need to know the refrence of every records, So I just add two Fields table_id and table_type and my final turnovers table is like id, amount, user_id,table_id, table_type.
if new record is about order record inserted like this
[1,25000,2,22,order]
and if new record is about increment credit like this
[1,25000,2,23,credit]
note
if using M:M tables its take so much time two retrieve the records
and my way
Cons is turnovers table records number is grows up
Pons is more flexible in new records and readable and search ability
nathan_jr's 4th option (model an entity that acts as a base for both Users and Groups, and have tickets owned by that entity) doesn't enforce referential integrity on PartyId. You'd have to do that on the application layer which invites all sorts of trouble. Can't really call it an antipattern when django's genericforeignkey implements the same solution, but no doubt you can design something more robust and performant using your framework's orm (using something like django's Multi-table inheritance)
CREATE TABLE dbo.OwnerType
(
ID int NOT NULL,
Name varchar(50) NULL
)
insert into OwnerType (Name) values ('User');
insert into OwnerType (Name) values ('Group');
I think that would be the most general way to represent what you want instead of using a flag.

Resources