updating tables while preserving business rules - sql

i have the following scenario , business rules and database schema , i want to update customer and salesrep tables by using just SQL while preserving business rules.how can i do that.
scenario:
suppose Premier Products wants to give all their customer and sales
reps a Christmas bonus. All customers will have their balance owing reduced
by $100, and their amount on order increased by $50. The increased amount
on order flows on to their sales reps. But because of the limitation that a sales rep amount on order cannot exceed $1000 (BR4),
some sales reps will not get a bonus.
(a) how this can be achieved in pure SQL, while preserving all business rules.
BR1. The total amount owed by a customer cannot exceed their credit limit.
BR2. The sales rep's amount on order is incremented by the same amount as their customers.
BR3. All new customers are given a $100 credit, that is an initial balance of -$100.
BR4. A sales rep is not entitled to any commission on sales beyond $1000 on order.
/*-- SLSREP Table */
CREATE TABLE SLSREP
(SLSRNUMB DECIMAL(2),
TOTCOMM DECIMAL(7,2),
COMMRATE DECIMAL(3,2) CONSTRAINT CK_commrate CHECK( commrate BETWEEN 0 AND 1),
AMOUNTONORDER DECIMAL(9,2),
CONSTRAINT PK_SLSREP PRIMARY KEY (SLSRNUMB));
/*-- Customer Table */
CREATE TABLE CUSTOMER
(CUSTNUMB DECIMAL(3),
BALANCE DECIMAL(7,2) ,
CREDLIM DECIMAL(5) CONSTRAINT NN_credlim NOT NULL ,
SLSRNUMB DECIMAL(2) ,
AMOUNTONORDER DECIMAL(9,2) ,
CONSTRAINT PK_CUSTOMER PRIMARY KEY (CUSTNUMB),
CONSTRAINT FK_CUSTOMER_slsrnumb FOREIGN KEY (SLSRNUMB) REFERENCES SLSREP,
CONSTRAINT CK_credit CHECK (balance + AMOUNTONORDER <= credlim));

These requirements seem to be in conflict.
Premier Products wants to give all
their customer and sales reps a
Christmas bonus.
But because of the limitation that a
sales rep amount on order cannot
exceed $1000 (BR4), some sales reps
will not get a bonus.
BR4. A sales rep is not entitled to
any commission on sales beyond $1000
on order.
I would model a bonus as a bonus, not as a commission on sales. A bonus isn't a commission or an order; they're all different things. So I propose two new business rules.
BR5. A bonus is not a commission.
BR6. A bonus is not an order.
Here's one way to implement a table of sales rep bonuses.
CREATE TABLE SLSREPBONUS (
SLSRNUMB DECIMAL(2) REFERENCES SLSREP (SLSRNUMB),
BONUSDT DATE,
BONUSAMT DECIMAL(7,2) NOT NULL CHECK (BONUSAMT BETWEEN 1 AND 1000),
CONSTRAINT PK_SLSREP PRIMARY KEY (SLSRNUMB, BONUSDT)
);
Your customer table has only aggregate amounts, so I'd assume that you'd have to issue a credit in some other part of your accounting system. I'd hope that if you did that, the customers aggregates would be updated properly. (But I have to say I've learned that hope doesn't scale well.)

Related

How to handle deposit to account in double entry book keeping?

as far as I understand double entry book keeping consists of two pairs, debit and credit.
when someone gets credit the other person should get debit ( and sum should always be 0 ).
My question is what if a person wants to deposit inside his/her own account, how that is going to work? should we add both credit and debit for the account? if so how are we able to determine the balance of the account then?
My tables:
users (
id SERIAL PRIMARY KEY,
username VARCHAR(40) NOT NULL UNIQUE,
password VARCHAR(40) NOT NULL,
created_at DATE DEFAULT CURRENT_DATE
)
transaction_types (
id SERIAL PRIMARY KEY,
name VARCHAR(20)
)
transactions (
id SERIAL PRIMARY KEY,
type_id INTEGER REFERENCES transaction_types(id),
user_id INTEGER REFERENCES users(id),
amount DECIMAL(20, 10) NOT NULL,
created_at DATE DEFAULT CURRENT_DATE
)
When a transaction between two users happens, a row for giver with negative amount and a row for receiver with positive amount is inserted. what should happen when a user deposits to his own account?
I think I'm misunderstanding something here.
Not programming, but accounting basics.
SOmeone depositing into an account is two things:
Adding money to your account
Adding a debt to the person (or a general accout "money owed to external parties", though most systems will have accounts per person there).
Note that if this is to pay for something you did, this debt immediately is offset by the negative value of the account (as it owes you money).

SQL/ORACLE- FOREIGN KEY using two columns from other tables

I'm trying to create an table in SQL*Plus that consults two columns from another table. For example,
If table A looks something like this:
CREATE TABLE Customers
(Customer_ID int NOT NULL PRIMARY KEY,
NAME Varchar(30) NOT NULL,
PHONE Varchar(12) NOT NULL,
OUTSTANDING_FEES Varchar(10) NULL);
And if my table B looks something like this:
CREATE TABLE Customer_Fees
(Fee_ID int NOT NULL PRIMARY KEY,
FEE_TYPE Varchar(20) NOT NULL,
AMOUNT Varchar(10) NOT NULL,
CUSTOMER_ID int NOT NULL);
I want to populate the OUTSTANDING_FEES in table A with the AMOUNT in table B, where the CUSTOMER_ID matches among the tables. For my purposes I can assume that any single Customer_ID in table B will only appear once in the table.
I've tried creating both tables, with the table A OUTSTANDING_FEES field being null and then making it a FOREIGN KEY that references table B's AMOUNT field, but it's not working since I need to make sure it also cross references the CUSTOMER_ID fields in both tables.
Thanks if you can help!
You can not create foreign key on a non-primary column from another table(s).
You'll have to create FK on FEE_ID.
CREATE TABLE Customers (
Customer_ID int NOT NULL PRIMARY KEY,
NAME Varchar(30) NOT NULL,
PHONE Varchar(12) NOT NULL,
OUTSTANDING_FEES Varchar(10) FOREIGN KEY REFERENCES Customer_Fees(FEE_ID)
);
You can use AMOUNT field in your select clause.
Select A.Customer_ID,B.AMOUNT from Customers A Join Customer_Fees B
on A.OUTSTANDING_FEES = B.FEE_ID
There are several things wrong with the data model posed in the question.
Defining OUTSTANDING_FEES and AMOUNT as varchar2columns is bad data modelling as both are surely intended to be numeric (monetary) values. Good practice is always to use the most appropriate datatype for the attribute we're modelling.
Building a foreign key between OUTSTANDING_FEES and AMOUNT is wrong because they are not unique identifiers. The amount of money owed by one customer can be the same as the amount of money owed by any other - even all - customers (at the start of term all students owe the same amount of tuition fees). So, a foreign which "references the CUSTOMER_ID fields in both tables" is all that is needed.
The data model doesn't provide any attribute which allows us to distinguish between fees which have been paid and fees which haven't.
The questioner states that "I can assume that any single Customer_ID in table B will only appear once in the table" but in real life we would expect Customers to have multiple fee records, unpaid and paid. Why not model that? Otherwise if there is truly a 1:1 relationship between Customer and Fee then there is no need for two tables.
So, here is an improved model. It uses proper datatype for monetary values; it enforces the foreign key between the two tables using CUSTOMER_ID; consequently it supports a one-to-many relationship between Customer and Fee; finally it tracks paid and unpaid fees.
create table customers
( customer_id integer not null constraint cust_pk primary key
, name varchar2(30) not null
, phone varchar2(12) not null
)
/
create table customer_fees
( fee_id integer not null constraint fees_pk primary key
, fee_type varchar2(20) not null
, amount number not null
, invoice_date date not null
, paid_date date null
, customer_id integer not null constraint fees_cust_fk references customers
)
/
Ah, but what about OUTSTANDING_FEES? Well, that information is derivable from the data in the two tables. There are many ways of writing this query, this approach is just a choice:
select cust.customer_id
, cust.name
, cust.phone
, fees.outstanding_fees
from customers cust
left outer join
( select fees.customer_id
, sum(case when fees.paid_date is null then fees.amount
else 0 end) as outstanding_fees
from customer_fees fees
group by fees.customer_id ) fees on fees.customer_id = cust.customer_id
/
Generally it is better to calculate aggregated values on demand rather than re-calculate them in every transaction. It scales better, certainly with OLTP volumes of data; the physics of a data warehouse is different, but I don't think that's what we're dealing with in this case.

How to count values for one id in table and put data to other table sql

I have problem with summing values... Tables are presenting like this:
create table clients
(
id_c INTEGER not null,
name VARCHAR2(20),
age INTEGER,
address VARCHAR2(20),
price number,
Primary key (id_c)
);
create table PRODUCTS
(
id_p NUMBER not null,
name_product VARCHAR2(30),
price NUMBER,
duration NUMBER,
primary key (id_p)
);
create table TRANSACTIONS
(
id_t NUMBER not null,
id_c NUMBER not null,
id_p NUMBER not null,
td_c NUMBER,
primary key (ID_t),
foreign key (ID_c) references CLIENTS (ID_c)
);
now i want to sum up all costs of products (translations.td_c) for each client (translations.id_c), and put that value to table clients into price row. The point is to take the same ID of client (ID_C) and sum up every row that have that ID_C in transactions table, sum up for that client every value of td_c, and put it into table clients to price column... I don't know how to do that, every time when i trying to make some UPDATE code, there are errors...
Try it with a subquery:
UPDATE clients
SET price = (SELECT sum(transactions.td_c)
FROM transactions
WHERE transactions.id_c = clients.id_c);
But your design is probably problematic. clients.price entirely depends on transactions and therefore shouldn't exist materialized like that.
I'll immediately go out of sync, if there are any changes in transactions. E.g., if you insert a new row into transactions for some customer "John" with an td_c of +100, the price for "John" in the clients table will be a 100 short until you update it again.
Consider using a view to have the price and the client data joined, always selecting the current price from transactions. E.g.:
CREATE VIEW clients_with_price
AS
SELECT c.id_c,
c.name,
c.age,
c.address,
s.price
FROM clients c
LEFT JOIN (SELECT sum(t.td_c) price,
t.id_c
FROM transactions t
GROUP BY t.id_c) s
ON s.id_c = c_id.c;
Then drop the price column in clients.

SQL Database Roomrate Approaches

I've developed a Hotel Management System some time ago. Difference is, that this system is used by several hotels and property owners who rent their homes in a single database. I can't just add prices to the roomtypes/accommodations as they might differ from one week or the other.
The approach i've used to overcome the issue of fixed stays (weekends, weeks, midweeks) for the mobile homes and the possibility to reserve hotelrooms (no-arrival days, minimum stays etc) is to store prices in a rate-table. Which is as follows:
tablename: Availability
int id
int roomTypeId
decimal rate
dateTime day
bit canArrive
int minimumStay
...
I am wondering, now the database is growing with more hotels and mobilehome's, if this approach is done properly or if there might be better ways instead of storing a rate for each roomtype and each date.
Sure!
It's important to note that, while rates can change daily, they usually don't (otherwise, any ads showing a rate would go rapidly obsolete - they could have been designed months before).
A simple, naive (first-iteration) design is as follows:
Hotel
=========
id -- autoincrement
name -- varchar(50)
contactInformation -- (address, phone, etc)
RoomType
==========
id -- autoincrement
description -- varchar(50)
HotelRoomTypeRate
==================
id -- autoincrement
hotelId -- fk reference to hotel.id
roomTypeId -- fk reference to roomType.id
rate -- decimal
effectiveOn -- date (this is 'business'/calendar day)
HotelRoom
===========
id -- autoincrement
hotelId -- fk reference to hotel.id
roomTypeId -- fk reference to roomType.id
status -- fk reference to status table, for things like 'under construction'
HotelCheckIn
==============
id -- autoincrement
hotelId -- fk reference to hotel.id
customerId -- fk reference to customer.id
hotelRoomId -- fk reference to hotelRoom.id
checkedInOn -- date (again, 'business'/calendar day)
HotelCheckOut
===============
id -- autoincrement
hotelCheckInId -- fk reference to hotelCheckIn.id
checkedOutOn -- date (again, 'business'/calendar day)
You would of course need to tweak this to suit your needs.

Advanced (?) SQL Joins?

I am a bit lost as to how to explain this, so I will try to give an example of some tables (+ data) and then the result that I am after (all my table columns are NOT NULL):
Table: Customers
Id int primary key
Name varchar(250)
Table: Stats (Date, CustomerId is the primary key)
Date (date)
CustomerId (int) - foreign key to Customers table
Earning (money)
Table: Bonus
Id int primary key
CustomerId int - foreign key to Customers table
Date date
Amount money
Table: Payments
Id int primary key
DateFrom date,
DateTo date,
CustomerId bigint - foreign key to Customers table
Table: CampaignPayment
Id int primary key
PaymentId int - foreign key to payments table
Quantity int
UnitPrice money
Table: BonusPayment
Id int primary key
PaymentId int - foreign key to payments table
Amount money
The idea here is that everytime a customer does something that is supposed to earn them money, it goes into the stats table. Customers can also receive different kinds of bonuses which goes into the bonus table. Every so often I need to create an invoice for the customers (Payments table) which will list the stuff from the stats table + the bonus table within the specified time period and that will generate the invoice (that is the payments table defines who the invoice is for, which period and the campaignpayment and bonuspayment table defines what is being paid and why).
Now - I need to be able to join all these tables up to be able to get an output of the following:
CustomerId | CustomerName | PaymentId | Amount | BonusAmount | DateFrom | DateTo
Amount is the summed Amount ( SUM(Quantity * UnitPrice) ) from the CampaignPayment table, and BonusAmount is the summed Amount ( SUM(Amount) ) from the BonusPayment table. DateFrom and DateTo is from the Payments table.
The trick is that for every customer within a given month where every single day of that month is not covered, I want a row with the following data:
CustomerId | CustomerName | NULL | (Stats.Earning - Amount Earned from possible payments within the month) | (Bonus.Amount - Amount Earned possible bonuses that is in payments within the month) | First day of month | Last day of month
I may need a bit more of complex logic as to how to calculate the amount and bonus amount within these "empty" rows but as for now, that is what I need to begin with.
How would I go about this? I know how to get the "initial" bit done, but how would I go about adding in these "empty" rows? I hope I explained the problem well enough in detail and that you can see the idea here - if not let me know and I will try to explain further.
The database is MS SQL Server 2008.
EDIT: Also alternatively an "empty" row for every customer per month is also and acceptable solution.
I'd make an auxiliary table with "every single day of that month" to ease identifying if "every single day of the month is not covered" (a somewhat ambiguous spec, but the aux table should help whether you mean "no day is covered" or "some days are not covered" and whether a day is considered "covered" if it has either a bonus or stats, or if it needs to have both to be considered "covered" -- these ambiguities are why I'm not going to even try and sketch the SQL using this aux table;-). Then I'd UNION the "empty rows" to the "initial bit" that you already know how to get done -- seems a perfect task for UNION!-)