Update Trigger Query - sql

I am using SQL Server 2014 with a table named dbo.ClientRecords. In this table I have a DateOfBirth field set as DATETIME and an Age field set as Nvarchar(40) at the moment. I know that this needs to be changed to int and I will do this.
I have the following query that essentially does a calculation based on the DateOfBirth field and the current date to work out the Age of the person. This works as I require it to.
SELECT id, DateOfBirth,
GETDATE() As [Today],
DATEDIFF (YY,DateOfBirth,GETDATE()) -
CASE
WHEN DATEADD(YY,DATEDIFF(YY,DateOfBirth,GETDATE()),DateOfBirth)
> GETDATE() THEN 1
ELSE 0
END AS [Age]
FROM dbo.ClientRecords
I am following the below website to create an update trigger as I need this update the age field once the insert statement for the web form has been submitted.
Tutorial about Triggers
This is my trigger that im trying to create, but I just cant seem to get this working.
CREATE TRIGGER [dbo].[Age_INSERT]
ON [dbo].[ClientRecords]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #id DATETIME
DECLARE #Age Varchar(40)
SELECT id,DateOfBirth,
GETDATE() As [Today],
DATEDIFF (YY,DateOfBirth,GETDATE()) -
CASE
WHEN DATEADD(YY,DATEDIFF(YY,DateOfBirth,GETDATE()),DateOfBirth)
> GETDATE() THEN 1
ELSE 0
END AS [Age]
FROM dbo.ClientRecords
INSERT INTO ClientRecords
VALUES(#id, #Age)
END
The error message that I get is the following.
Msg 213, Level 16, State 1, Procedure Age_INSERT, Line 21
Column name or number of supplied values does not match table definition.
I'm failing as im not to sure what I need to set as a variable. I thought that I would need the id from the ClientRecords table and the Age field.

Related

SQL query for user input

I wrote a SQL query that takes input (customer id via an application) from a user and returns the record but if the id is zero it displays "Invalid ID". The query I have written does show the data correctly when i set an id (SET #CID = ###) but not sure if this is the correct way to do it. Also how can I restrict the user to input a minimum of three digits. Below is the query
USE WideWorldImporters
GO
DECLARE #CID int;
SET #CID = #CID
IF #CID > 0
BEGIN
SELECT CustomerID, sum(TransactionAmount) as TotalAmount
FROM Sales.CustomerTransactions
Where CustomerID = #CID
Group by customerID
RETURN
END
ELSE
Begin
PRINT 'Invalid ID'
END;
Based on your current code. I make the assumption that CustomerID is actually an int. Meaning excess 0 infront of the number are not present. (I.e. 0011 is not an actual ID. Since the column is numeric it will be stored as 11).
With that in mind, you can write the current code into a procedure like such, that will not do a select under the circumstances you described (NULL, minimum of 3 digits and not less than 0).
CREATE PROCEDURE dbo.foo
#CID INT
AS
BEGIN
IF (#CID >= 1000) --This works because it's a numeric column. NULL is not greater than 1000 either.
--And anything less than 1000 would not be 3 digits.
BEGIN
SELECT CustomerID, SUM(TransactionAmount) as TotalAmount
FROM Sales.CustomerTransactions
WHERE CustomerID = #CID
GROUP BY CustomerID
END
ELSE
BEGIN
--SELECT 'Invalid ID' Use this if you want a single data row to be returned.
-- Note this column would be a varchar!
PRINT 'Invalid ID' -- Print goes to the output window,
-- not the results window like a query would.
END
END
Execution in SQL server would be done as
EXEC dbo.foo 1100
This because there's only 1 parameter, so you don't have to identity it.
Alternatively the way to call a procedure and explicitly assign parameter values is.
EXEC dbo.foo #CID = 1100

Manually Checking of Value Changes in Tables for SQL

An example to the problem:
There are 3 columns present in my SQL database.
+-------------+------------------+-------------------+
| id(integer) | age(varchar(20)) | name(varchar(20)) |
+-------------+------------------+-------------------+
There are a 100 rows of different ids, ages and names. However, since many people update the database, age and name constantly change.
However, there are some boundaries to age and name:
Age has to be an integer and has to be greater than 0.
Name has to be alphabets and not numbers.
The problem is a script to check if the change of values is within the boundaries. For example, if age = -1 or Name = 1 , these values are out of the boundaries.
Right now, there is a script that does insert * into newtable where age < 0 and isnumeric(age) = 0 or isnumeric(name) = 0;
The compiled new table has rows of data that have values that are out of the boundary.
I was wondering if there is a more efficient method to do such checking in SQL. Also, i'm using microsoft sql server, so i was wondering if it is more efficient to use other languages such as C# or python to solve this issue.
You can apply check constraint. Replace 'myTable' with your table name. 'AgeCheck' and 'NameCheck' are names of the constraints. And AGE is the name of your AGE column.
ALTER TABLE myTable
ADD CONSTRAINT AgeCheck CHECK(AGE > 0 )
ALTER TABLE myTable
ADD CONSTRAINT NameCheck CHECK ([Name] NOT LIKE '%[^A-Z]%')
See more on Create Check Constraints
If you want to automatically insert the invalid data into a new table, you can create AFTER INSERT Trigger. I have given snippet for your reference. You can expand the same with additional logic for name check.
Generally, triggers are discouraged, as they make the transaction lengthier. If you want to avoid the trigger, you can have a sql agent job to do auditing on regular basis.
CREATE TRIGGER AfterINSERTTrigger on [Employee]
FOR INSERT
AS
BEGIN
DECLARE #Age TINYINT, #Id INT, Name VARCHAR(20);
SELECT #Id = ins.Id FROM INSERTED ins;
SELECT #Age = ins.Age FROM INSERTED ins;
SELECT #Name = ins.Name FROM INSERTED ins;
IF (#Age = 0)
BEGIN
INSERT INTO [EmployeeAudit](
[ID]
,[Name]
,[Age])
VALUES (#ID,
#Name,
#Age);
END
END
GO

Procedure returning 0 instead of higher number

I have the following procedure to retrieve some data, based by the year, which is input by the user. However, I always get a 0 back. I'm still fairly new to SQL, but this seemed like it should work
Create PROCEDURE [dbo].[Yearly]
#year int
AS
BEGIN
DECLARE #yearly Datetime
DECLARE #summ int
SELECT #summ = SUM([dbo].[Out].[OutPcs]), #yearly = [dbo].[Out].[DateTime]
FROM [dbo].[Out]
WHERE YEAR(#yearly) = #year
GROUP BY [Out].[DateTime]
END;
Should I have used nested select statements? I suspect something is wrong in that part of the procedure.
You have DECLARE #yearly Datetime.
You attempt to set it in SELECT ... #yearly = Out.Datetime FROM Out, but then you have this WHERE statement: YEAR(#yearly) = #year
This returns nothing since #yearly is NULL when called by YEAR()
This makes the statement equivalent to WHERE NULL = 2018
Which will never be true.
To fix this, you need to set yearly before calling it in your WHERE clause or use something else there.
It looks like you want to use YEAR(Dbo.Out.Datetime) instead there
Since it looks like you're new to SQL I will add some extra explanation. This is an oversimplification.
Most programming languages run top to bottom. Executing the line1 first, line2 second, line3 third, and so on. SQL does not do this.
The command SELECT Name FROM Employee WHERE EmpID = 1 Runs in the following order.
First - FROM Employee --> Load the Employee table
Second - WHERE EmpID = 1 --> Scan Employee for the records where EmpID = 1
Third - SELECT Name --> Display the `Name` field of the records I found.
Your command looks like this to the SQL compiler
First - FROM dbo.Out --> Load Out table
Second - WHERE YEAR(#yearly) = #year --> Scan for records that meet this req.
Third - SELECT ... #yearly = dbo.Out.Datetime --> Set #yearly to the [Datetime] field associated to the record(s) I found.
Note that if your statement had returned multiple records, then SQL would have tried to set your 1-dimensional variable to an array of values. It would fail and give you something like
Too many records returned. Have me only return 1 record.
Why your code is not working is well explained by #Edward
Here is a working code:
Create PROCEDURE [dbo].[Yearly]
#year int
AS
BEGIN
SELECT SUM([dbo].[Out].[OutPcs])
FROM [dbo].[Out]
WHERE YEAR([dbo].[Out].[DateTime]) = #year
END;
You forgot to return "summ":
And #yearly var is not necessary.
Group by Year is not necessary too.
Create PROCEDURE [dbo].[Yearly]
#year int
AS
BEGIN
DECLARE #summ int
SELECT #summ = SUM([dbo].[Out].[OutPcs])
FROM [dbo].[Out]
WHERE YEAR([dbo].[Out].[DateTime]) = #year
Return #summ
END;

How do check if a time is greater than a particular one?

I am building a windows form application and for my database I want to create a procedure to insert values. For this procedure I want to check if the time is later than 8:45 then you are considered late.
Here is what I was trying so far
Create Procedure "Insert Attendance"
(
#att_id int,
#emp_id integer,
#work_date date,
#expected_time time(7)
#time_in time(7),
#time_out time(7),
#time_elapse time(7),
#time_status varchar(20)
)
As
Begin
If ((time_in=#time_in)>16:45)
then set time_status = 'Late'
Begin
Insert Into attendance
Values
(#att_id,#emp_id,#work_date,#expected_time,#time_in,#time_out,#time_elapse,#time_status)
End
Else
Begin
Select 'Error'
End
End
You can use string constants to define the time to compare to:
...
IF #time_in > '08:45'
BEGIN
SET time_status = 'Late';
...
Also note, that in SQL Server there is no THEN for IF.

SQL stored procedure IF EXISTS UPDATE ELSE INSERT

OK. I got a lot of help here earlier working with a SQL backend to a simple ... just not for me :( ... time clock solution for the small office I work in, so I'm back for more!
My table I'm currently working with consists of 6 columns:
clockDate date not null PK
userName varchar(50) not null PK
clockIn time(0)
breakOut time(0)
breakIn time(0)
clockOut time(0)
I though I had figured out my IF NOT EXISTS INSERT ELSE UPDATE statement from my last question, but now I'm trying to use it in a Stored Procedure, rather than a plain query window, with no success.
Basically a user clocking in is a no-brainer. However, if the user doesn't clock in, but they clock out for lunch, the statement needs to create the row instead of updating an existing row. Ok so here's my stored procedure:
ALTER PROCEDURE dbo.BreakOut
(
#userName varchar(50)
)
AS
IF EXISTS (SELECT * FROM Clock WHERE clockDate = GETDATE() AND userName = #userName)
BEGIN
UPDATE Clock SET breakOut = GETDATE()
WHERE clockDate = GETDATE() AND userName = #userName
END
ELSE
BEGIN
INSERT INTO Clock (clockDate, userName, breakOut)
VALUES (GETDATE(), #userName, GETDATE())
END
Here's my problem... If the user DID clock in for the day I get a primary key violation because the stored procedure is still trying to run the INSERT part of the statement and never runs the UPDATE line. I've tried it flipped with an IF NOT EXISTS as well with the same result. What's the trick to get IF-ELSE to work in a stored procedure? Can this be done they way I'm thinking or do I have to study Merge statement? My plan is to run the stored procedures from a simple Visual Basic program on each workstation. Maybe I'm getting in over my head :( To bad my boss is too cheap to just buy a time clock solution!
EDIT:
Thank you ALL for your help!! I'm falling in love with this site, questions get answers SO FAST!!! Here is my working stored procedure:
ALTER PROCEDURE dbo.BreakOut
(
#userName varchar(50)
)
AS
IF EXISTS (SELECT * FROM Clock WHERE DateDiff(dd, GetDate(),clockDate) = 0 AND userName = #userName)
BEGIN
UPDATE Clock SET breakOut = GETDATE()
WHERE DateDiff(dd, GetDate(),clockDate) = 0 AND userName = #userName
END
ELSE
BEGIN
INSERT INTO Clock (clockDate, userName, breakOut)
VALUES (GETDATE(), #userName, GETDATE())
END
Is this proper, or could it be improved more? Again Thank You ALL SO MUCH!!!
This is probably the problem right here: WHERE clockDate = GETDATE()
GetDate returns the current date AND the current time, which wouldn't match up with clockDate. You can compare the dates with DateDiff instead:
WHERE DateDiff(dd, GetDate(),clockDate) = 0
Your problem would appear to be the following:
Let's imagine the user clocked in at 09:00
A record like the following might exist:
ClockDate userName clockIn breakOut breakIn clockOut
12/08/2012 joe 09:00 NULL NULL NULL
Now your IF statement is doing this:
SELECT * FROM Clock WHERE clockDate = "20120812 17:24:13" AND userName = #userName
i.e. this record wont exist.
Instead, try this:
IF EXISTS (SELECT * FROM Clock WHERE clockDate = DATEADD(D, 0, DATEDIFF(D, 0, GETDATE())) AND userName = #userName)
You also need to make sure you are storing clockDate as just the date portion of GETDATE(), otherwise, you would need to adjust your query like so:
IF EXISTS (SELECT * FROM Clock WHERE DATEADD(D, 0, DATEDIFF(D, 0, clockDate)) = DATEADD(D, 0, DATEDIFF(D, 0, GETDATE())) AND userName = #userName)
Your update will never run because GETDATE returns a date and time.
http://msdn.microsoft.com/en-us/library/ms188383.aspx
CREATE PROCEDURE `SP_GENRE_SELECT`(
IN _Id INTEGER,
IN _Name VARCHAR(50),
IN _account VARCHAR (50),
IN _Password VARCHAR (50),
IN _LastConnexionDate DATETIME,
IN _CreatedDate DATETIME,
IN _UpdatedDate DATETIME,
IN _CreatedUserId INTEGER,
IN _UpdatedUserId INTEGER,
IN _Status TINYINT
)
BEGIN
SELECT *
FROM user
WHERE Id LIKE IF(_Id IS NULL,'%',CAST(_Id AS VARCHAR(50)))
AND
Name LIKE IF(_Name IS NULL,'%',CONCAT('%',_Name,'%'))
AND
Account LIKE IF(_Account IS NULL,'%',CONCAT('%',_Account,'%'))
AND
LastConnexionDate LIKE IF(_LastConnexionDate IS NULL,'%',CONCAT('%',CAST(LastConnexionDate AS VARCHAR(50),'%')))
AND
CreatedDate LIKE IF(_CreatedDate IS NULL,'%',CONCAT('%',CAST(_CreatedDate AS VARCHAR(50),'%')))
AND
UpdatedDate LIKE IF(_UpdatedDate IS NULL,'%',CONCAT('%',CAST(_UpdatedDate AS VARCHAR(50),'%')))
AND
CreatedUserID LIKE IF(_CreatedUserID IS NULL,'%',CONCAT('%',CAST(_CreatedUserID AS VARCHAR(50),'%')))
AND
UpdatedUserID LIKE IF(_UpdatedUserID IS NULL,'%',CONCAT('%',CAST(_UpdatedUserID AS VARCHAR(50),'%')))
AND
Status LIKE IF(_Status IS NULL,'%',CAST(_Status AS VARCHAR(50),'%'))
END