I'm currently doing a banking website for my project, using C# and asp.net. One of the function is 'Remaining Daily Limit'. I need the database value to change back to default once I changed the system date to another day. Example : If a user had $500 (default value) as a daily limit, and he used all up. The next day, he will have $500 again. May I know how should I go about it?
Here is a SQL script that will create how I would setup your db with some sample data:
CREATE TABLE tblBankCustomer
(
BankCustomerId INT NOT NULL IDENTITY(1,1) PRIMARY KEY
, FirstName NVARCHAR(100) NOT NULL
, LastName NVARCHAR(100) NOT NULL
, DailySpendingLimit DECIMAL(38, 2) NOT NULL CONSTRAINT DF_tblBankCustomer_DailySpendingLimit DEFAULT(500)
)
CREATE TABLE tblTransactionType
(
TransactionTypeId INT NOT NULL IDENTITY(1,1) PRIMARY KEY
, TransactionType VARCHAR(50) NOT NULL
)
INSERT tblTransactionType (TransactionType)
VALUES ('Deposit')
, ('Withdrawal')
CREATE TABLE tblTransaction
(
TransactionId INT NOT NULL IDENTITY(1,1) PRIMARY KEY
, BankCustomerId INT NOT NULL
, TransactionDate DATE NOT NULL
, Amount DECIMAL(38, 2) NOT NULL
, TransactionTypeId INT NOT NULL
)
ALTER TABLE tblTransaction
ADD CONSTRAINT FX_tblTransaction_tblBankCustomer
FOREIGN KEY (BankCustomerId)
REFERENCES tblBankCustomer(BankCustomerId)
ALTER TABLE tblTransaction
ADD CONSTRAINT FX_tblTransaction_tblTransactionType
FOREIGN KEY (TransactionTypeId)
REFERENCES tblTransactionType(TransactionTypeId)
INSERT tblBankCustomer
(
FirstName
, LastName
)
VALUES ('Jeremy', 'Pridemore')
, ('K', 'YQ')
INSERT tblTransaction
(
BankCustomerId
, TransactionDate
, Amount
, TransactionTypeId
)
VALUES
(1, CURRENT_TIMESTAMP, 48.50, 2) -- Jeremy, Today, $48.50, Withdrawal
, (1, CURRENT_TIMESTAMP, 300.00, 2) -- Jeremy, Today, $300, Withdrawal
, (1, CURRENT_TIMESTAMP, -200.00, 1) -- Jeremy, Today, $200, Deposit
, (2, CURRENT_TIMESTAMP, 285.00, 2) -- K, Today, $285, Withdrawal
, (2, CURRENT_TIMESTAMP, 215.00, 2) -- K, Today, $215, Withdrawal
GO
CREATE FUNCTION fGetRemainingSpendingLimit
(
#BankCustomerId INT
, #Date DATE
)
RETURNS DECIMAL(38, 2)
BEGIN
SET #Date = ISNULL(#Date, CURRENT_TIMESTAMP)
DECLARE #RemainingLimit DECIMAL(38, 2) =
(SELECT
SUM([Transaction].Amount)
FROM tblBankCustomer Customer
INNER JOIN tblTransaction [Transaction]
ON [Transaction].BankCustomerId = Customer.BankCustomerId
AND [Transaction].TransactionDate = #Date
INNER JOIN tblTransactionType TransactionType
ON TransactionType.TransactionTypeId = [Transaction].TransactionTypeId
AND TransactionType.TransactionType = 'Withdrawal'
WHERE Customer.BankCustomerId = #BankCustomerId)
RETURN #RemainingLimit
END
GO
-- Some sample selects
SELECT dbo.fGetRemainingSpendingLimit(1, NULL)
SELECT dbo.fGetRemainingSpendingLimit(2, NULL)
Then in C# you should know the customer's ID for the customer you're working with. If you're using somethiing like ADO.NET you can call this function directly and use the value in code.
Default values are usually stay in some config file. In your specific case, I would say that you can
or have a table with default values
or in the tables where is possible to have restore functionality have default column with corresponding values.
after, can restore them using some stored procedure.
You can create SQL job and schedule it to run every midnight, within the job you can execute a stored procedure reseting values to $500.
In my opinion, based on that there may be other requirements in the future, I'd have a table that looks like:
UserID AppliedOn Limit
1 1/1/2012 500
1 2/1/2012 750
This give you a historic view of limits, that you can give to the user or data-mine. In the same way, that is how I would apply current daily limits.
UserID AppliedOn Withdrawn
1 1/10/2012 125
1 1/10/2012 225
Now on 1/1/2012 it would be easy to determine what the amount left in the limit is without any jobs or triggers. And again you'd have historic values that can be data-mined for other features.
SELECT
ul.Limit - uw.Sum as LimitLeft
FROM
UserLimit ul
INNER JOIN (
SELECT
UserID,
AppliedOn,
SUM(Limit) as Sum
FROM
UserLimit
Group by
UserID,
AppliedOn) uw on ul.UserID = uw.UserID
and ul.AppliedOn = uw.AppliedOn
WHERE
ul.UserID = #userID
AND ul.AppliedOn = #dateInQuestion
(my raw SQL skills might be a bit rusty due to Entity Framework here).
Related
So I have a Product table with a price.
I just want to update that price in a date interval.
Example:
apple is 10
And from 2018-02-02 to 2018-03-02 I want to update the price to 12.
I mention that I create a second table PriceInterval to insert the date interval.
enter code herePseudocode for my sqlscript
Hopefully I have understood the question correctly. A possible solution to what you're describing will mean changing the schema slightly. Having a new field in the product intervals for a new price between two dates allows you still keep the default price, when the interval is over. And using a query to select the price based on the date.
DROP TABLE IF EXISTS product_intervals;
DROP TABLE IF EXISTS products;
CREATE TABLE products (
ID INT NOT NULL AUTO_INCREMENT,
`Name` VARCHAR(50) NOT NULL,
Price DECIMAL(18,1) NOT NULL,
CONSTRAINT pkProductId PRIMARY KEY (ID)
);
CREATE TABLE product_intervals (
ID INT NOT NULL AUTO_INCREMENT,
ProductId INT NOT NULL,
DateTo DATETIME NOT NULL,
DateFrom DATETIME NOT NULL,
NewPrice DECIMAL(18,1) NOT NULL,
CONSTRAINT pkProductIntervalId PRIMARY KEY (ID),
CONSTRAINT fkProductId FOREIGN KEY (ProductId) REFERENCES `products`(`ID`) ON DELETE CASCADE
);
INSERT INTO products (`Name`, Price)
SELECT 'Apple', 0.10
UNION ALL SELECT 'Banana', 0.20;
INSERT INTO product_intervals (ProductId, DateTo, DateFrom, NewPrice)
SELECT 2, STR_TO_DATE('2018-02-10T00:00:00','%Y-%m-%dT%H:%i:%s'), STR_TO_DATE('2018-02-15T00:00:00','%Y-%m-%dT%H:%i:%s'), 0.30;
select p.`Name`, CASE WHEN pi.NewPrice IS NOT NULL THEN pi.NewPrice ELSE p.Price END AS Price
FROM products p
left join product_intervals pi ON p.ID = pi.ProductId AND DateFrom > NOW() AND DateTo < NOW();
The key part to this is the left join and the CASE statement in the select query. This code hasnt been fully tested, so it might need a bit of tweaking but hopefully will give you a good indication of what a solution could be.
I have created table like below:
create table EmployeeDetails
(
id int,
name varchar(50),
designation varchar(50),
ReportingTo int
)
And inserted rows like this:
insert into EmployeeDetails values(1, 'A', 'Developer', 3)
insert into EmployeeDetails values(5, 'E', 'CEO', 5)
insert into EmployeeDetails values(2, 'B', 'Developer', 3)
insert into EmployeeDetails values(3, 'C', 'Manager', 4)
insert into EmployeeDetails values(4, 'D', 'S.Manager', 5)
My question is: how can I identify the last inserted row in the table?
Used queries:
select IDENT_CURRENT('EmployeeDetails')
Select SCOPE_IDENTITY()
But I still didn't find the answer.
Replies are always welcome
You have a fundamental misunderstanding of tables in relational databases. They represent unordered sets. So, there is no "last row" in a table. The information on the ordering of rows has to be included when you create the table and/or load data into it.
In SQL Server, the simplest method is an identity column. That is why many tables are defined as:
create table EmployeeDetails (
id int identity primary key,
name varchar(50),
designation varchar(50),
ReportingTo int
);
You can also add default insertion time columns:
create table EmployeeDetails (
id int identity primary key,
name varchar(50),
designation varchar(50),
ReportingTo int,
CreatedAt datetime default gettime()
);
However, you can have multiple rows with the same datetime value.
You also can use rowversion like below,if you add a column
CREATE TABLE MyTest (myKey int
,myValue int, RV rowversion);
GO
INSERT INTO MyTest (myKey, myValue) VALUES (1, 0);
GO
INSERT INTO MyTest (myKey, myValue) VALUES (2, 0);
select * from mytest order by rv desc
Note:
This always give the last row inserted/Updated
References:
rowversion
Add fields of type datetime with name CreateDate and UpdateDate in your table, when you insert record in your table set their value for getdate()
After that you can run queries:
Select top 10 * from YourTable Order By CreateDate DESC
Or for last updated
Select top 10 * from YourTable Order By UpdateDate DESC
Please find my answer as below. Hope this may help you.
Add one more column to store record creation date/time as below.
create table EmployeeDetails
(
id int,
name varchar(50),
designation varchar(50),
ReportingTo int,
CreatedOn datetime
)
After table creation and inserting records write/execute query (here inner query is used) as below
select
*
from EmployeeDetails
where CreatedOn = (select max(CreatedOn) from EmployeeDetails )
I have come up with scenario and i am really struggling with it. Please help me in this. i have table below and store procedure is calling for this table on button click in c# and data source is assign to grid.
Id Product QtyReceive QtyDispatch Location
1 HP2030 20 0 A
Below is procedure which is call on click event and it just select the require quantity of product which we require from specific location.
DECLARE #Data TABLE
(
Id int identity(1,1)
, Product varchar(10)
, QtyReceive int
, QTYDispatch int
, Location varchar(10)
)
DECLARE #Qty int = 10
INSERT #Data VALUES
('HP2030', 20 ,5,'A');
WITH sumqty AS
(
SELECT *, SUM(QtyReceive -QTYDispatch) OVER (PARTITION BY Product ORDER BY Id) AS TotalQty FROM #Data
)
,takeqty AS (
SELECT *,
CASE
WHEN #Qty >= TotalQty THEN QtyReceive
ELSE #Qty - ISNULL(LAG(TotalQty) OVER (PARTITION BY Product ORDER BY Id), 0)
END AS TakeQty
FROM sumqty
)
SELECT
Product
, TotalQty
, TakeQty
, Location
FROM takeqty WHERE TakeQty > 0
ORDER BY Location;
Now there is a problem let say two user sitting at two different computer and User 1 required 10 quantity and other user required 5 quantity. They click button to get quantity at same time and save it at same time and dispatch quantity update with quantity 10 instead of 10+5=15. It happens when both user hit save at same time. No of user can be vary. Is there any solution to this problem?
I try to understand the whole scenario.
I'm using WPF MVVM Pettern and
I Have a simple table on SQL Server 2012 which its ID(key) column is computed in an StoredProcedure, called PersonInsert: (This is simplified, but what is computed is more complex than this, anyway it's an int at last)
USE [Guard]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[PersonInsert]
#FName NVARCHAR(50) ,
#LName NVARCHAR(50) ,
#ID INT = NULL OUTPUT
AS
BEGIN
BEGIN TRY
SELECT #ID = ISNULL(MAX(ID), 0) + 1
FROM dbo.Person
INSERT INTO [dbo].[Person]
( [ID] ,
[FName] ,
[LName]
)
VALUES ( #ID ,
#FName ,
#LName
)
END TRY
BEGIN CATCH
DECLARE #ErrMsg NVARCHAR(MAX) ,
#ErrSvr INT ,
#ErrStt INT
SELECT #ErrMsg = ERROR_MESSAGE() + '|' + ERROR_PROCEDURE() + '|'
+ CAST(ERROR_LINE() AS NVARCHAR(5)) ,
#ErrSvr = ERROR_SEVERITY() ,
#ErrStt = ERROR_STATE()
RAISERROR (#ErrMsg, #ErrSvr, #ErrStt)
END CATCH
END
GO
In the .net side, I use EF 6.1 Code-First to handle data and mapped to SPs so I have OnModelCreating like this:
modelBuilder.Entity(Of Person)().MapToStoredProcedures(
Sub(x)
x.Insert(Function(e) e.HasName("[dbo].[PersonInsert]"))
x.Update(Function(e) e.HasName("[dbo].[PersonUpdate]"))
x.Delete(Function(e) e.HasName("[dbo].[PersonDelete]"))
End Sub)
And My Model is:
<Table("Person")> _
Partial Public Class Person
<DatabaseGenerated(DatabaseGeneratedOption.None), Key> _
Public Property ID As Integer
Public Property FName As String
Public Property LName as String
End Class
Now the strange thing is, when I try to Insert Data (Adding New Person) at first time, the db.SaveChanged() works great, but for second time it throws InvalidOperation exception with a message:
The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: Saving or accepting changes failed because more than one entity of type 'Shoniz.Guard.WPFGuardApplication.Person' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration.
He is right! the data is comitted successfully! and I'm right too, because my ID(key) column is computed right and it's completely unique. Either I have used the DatabaseGeneratedAttribute with none value on ID, but the result is the same :(
Even when I re-query the db to check about duplicate keys, I find NOTHING!
Why this exception is thrown?
How can I prevent that?
Is there anyway to Ignore the changes after db.SaveChanges()?
First of all change DatabaseGeneratedOption.None to DatabaseGeneratedOption.Indetity
and finally change stored procedure to this:
BEGIN TRY
SELECT #ID = ISNULL(MAX(ID), 0) + 1
FROM dbo.Person
INSERT INTO [dbo].[Person]
( [ID] ,
[FName] ,
[LName]
)
VALUES ( #ID ,
#FName ,
#LName
)
--You have to tell EF this is returned Id of inserted person
SELECT #ID as ID
END TRY
After reading up on some normalization, it seems I am not quite grasping the concept.
What I am attempting to do is create a table that holds information for an item raffle.
So information for the item is ItemName, and Defindex.
After that, I need to have the "tickets" for the raffle, and to which user they went to. I want to limit the number of tickets sold.
So TicketA, TicketB, TicketC, TicketD.
Is there a query that I can use to insert a player name into TicketA if it's not full, or TicketB if A is full, so on until all tickets are sold?
Here's a sample to get you started: SQL Fiddle
Setup the basic tables to keep track of raffles and tickets.
CREATE TABLE Raffle
(
Id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY
,Name VARCHAR(50) NOT NULL
,WinnerLimit INT NOT NULL
);
CREATE TABLE RaffleTicket
(
Id BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY
,RaffleId BIGINT NOT NULL
,UserId BIGINT NOT NULL
);
Setup a view to determine whether a raffle has any tickets left.
CREATE VIEW RaffleStatus
AS
SELECT Id AS RaffleId,
CASE WHEN EXISTS
(
SELECT 1
FROM RaffleTicket rt
WHERE rt.RaffleId = r.Id
GROUP BY rt.RaffleId
HAVING COUNT(rt.RaffleId) = r.WinnerLimit
)
THEN 1
ELSE 0
END AS SoldOut
FROM Raffle r;
Create a procedure to prevent overselling tickets. Note this probably isn't safe for multithreaded use.
DELIMITER //
CREATE PROCEDURE InsertRaffleTicket (IN userId BIGINT, IN raffleId BIGINT)
BEGIN
INSERT INTO RaffleTicket(UserId, RaffleId)
SELECT userId, raffleId
FROM RaffleStatus rs
WHERE rs.RaffleId = raffleId
AND rs.SoldOut = 0;
END//
DELIMITER ;
I want to limit the number of tickets sold.
That is business logic, not something that should reside in your database.
What if you want to query all raffles user X has attended? select * from raffles where ticketA = 'UserX' or ticketB = 'UserX' or ticketC = 'UserX'...
Just normalize it and check the ticket count from code.
One table of people, one of tickets, one of raffles.
Each ticket has the ID of the person and of the raffle.
As the previous answer, beyond that is business logic that shouldn't be in the DB. At most, you should "release" a batch of tickets for a raffle by creating them in the tickets table and then when a person wants to buy one you take the first ticket with the correct raffle ID and a null person ID.