TRIGGER AFTER INSERT SELECT MIN(COUNT) insert ID - sql-server

I'm trying to create a trigger after an insert on the eventss table. The trigger should select the Bcoordinator_ID from the bookingCoordinator table where they have the minimum number of occurrences in the eventss table.
Here's my table data followed by the trigger. It doesn't like the minCount in the values, I think it's looking for and int.
DROP TABLE eventsBooking
CREATE TABLE eventsBooking
(
EBK INT NOT NULL IDENTITY(100, 1),
booking_ID AS 'EBK'+CAST( ebk as varchar(10)) PERSISTED PRIMARY KEY,
bookingDate DATE,
Bcoordinator_ID VARCHAR (20),
eventss_ID VARCHAR (20) NOT NULL
)
INSERT INTO eventsBooking
VALUES ('2015-01-07 11:23:00', NULL, 'EVT100');
Eventss table:
EVT INT NOT NULL IDENTITY(100, 1),
eventss_ID AS 'EVT' + CAST(evt as varchar(10)) PERSISTED PRIMARY KEY,
eventsName varchar(50),
noOfStages SMALLINT,
noOfRounds SMALLINT,
eventsDate DATE,
entryFee DECIMAL (7,2),
venue_ID VARCHAR (20) NOT NULL,
judges_ID VARCHAR (20)
INSERT INTO eventss
VALUES ('Swimming Gala 2015', '3', '7', '2015-01-07 09:00:00', '35.00', 'VEN101', 'JUD100');
CREATE TABLE bookingCoordinator
(
BCO INT NOT NULL IDENTITY(100, 1),
Bcoordinator_ID AS 'BCO'+CAST( bco as varchar(10)) PERSISTED PRIMARY KEY,
forename varchar(20) NOT NULL,
familyName varchar(50)
)
INSERT INTO bookingCoordinator VALUES ('Steve', 'Wills');
Trigger:
CREATE TRIGGER TRGinsertJudge
ON [dbo].[eventss]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.eventsBooking (Bcoordinator_ID, bookingDate, Eventss_ID)
VALUES(minCount, getdate(), 100)
SELECT MIN(COUNT(Bcoordinator_ID)) AS minCount
FROM eventsBooking
END

You can't do an aggregation of an aggregation i.e. MIN(COUNT(1))
If you just want the Bcoordinatior_ID with the least counts in eventsBooking, do this
select top 1 bcoordinator_id
from eventsBooking
group by bcoordinator_id
order by count(1) asc
And you don't use VALUES() in an INSERT INTO ... SELECT statement
Also, in your current code, since eventsBooking.bcoordinator_id is always null, you need to join to the actual table of bookingCoordinators to return booking coordinators without any events booked.
So your complete trigger statement should be
INSERT INTO dbo.eventsBooking (Bcoordinator_ID, bookingDate, Eventss_ID)
select
top 1
bookingcoordinator.bcoordinator_id, getdate(), 100
from bookingCoordinator left join eventsBooking
on bookingCoordinator.Bcoordinator_ID = eventsBooking.Bcoordinator_ID
group by bookingcoordinator.bcoordinator_id
order by count(1) asc

Related

GETDATE() Msg 241 Conversion failed when converting date and/or time from character string

This SQL statement throws an conversion error on one of my servers:
DECLARE #TimeStamp datetime;
SET #TimeStamp = GETDATE();
INSERT INTO Lookups
SELECT
newid() AS Id,
'Type' AS Type, Name,
'A.Miller' AS CreatedName,
'A.Miller' AS ChangedName,
#TimeStamp AS CreatedDate,
#TimeStamp AS ChangedDate
FROM
[DBServer].[Database].[dbo].[LkValues]
WHERE
NOT Name IS NULL OR Name = ''
Error:
Msg 241, Level 16, Status 1, Row 5
Conversion failed when converting date and/or time from character string.
I tried it with several variants as well wihout a variable to convert is us format and without converting, without success:
CONVERT(datetime, GETDATE(), 110) AS CreatedDate,
CONVERT(datetime, GETDATE(), 110) AS ChangedDate
A SELECT statement without INSERT results in values which looks all pretty well.
I am running it on a SQL Server 2008 R2 (v10.50.2550.0).
On another server the command runs without any problem, why?
Table DDL:
CREATE TABLE [dbo].[Lookups]
(
[Id] [uniqueidentifier] NOT NULL,
[Typ] [varchar](16) NOT NULL,
[Name] [varchar](32) NOT NULL,
[ChangedDate] [datetime] NULL,
[ChangedName] [varchar](32) NULL,
[CreatedDate] [datetime] NULL,
[CreatedName] [varchar](32) NULL,
CONSTRAINT [PK_dbo.Lookups]
PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY];
This is very much a guess at this time, however, I would guess that the order of the columns is not the same as that in the SELECT statement. Also, there's no need to assign the value of GETDATE() to a variable, you can simply state it in your SELECT.
Assuming that the aliases you have used are the names of the columns in your table [DBServer].[Database].[dbo].[LkValues] then this should work:
INSERT INTO Lookups (Id, [Type],[Name],CreatedName,ChangedName,CreatedDate,ChangedDate)
SELECT newid(),
'Type',
[Name],
'A.Miller',
'A.Miller',
GETDATE(),GETDATE()
FROM [DBServer].[Database].[dbo].[LkValues]
WHERE NOT [Name][] IS NULL OR [Name] = '';
Edit: My guess was correct. When inserting into a table, aliases mean nothing. It's about the order you provide the columns, not their names. For example:
CREATE TABLE #sample (column1 int, column2 int);
INSERT INTO #sample
--inserts into the "wrong" columns
SELECT 2 AS column2, 1 AS column1;
SELECT *
FROM #sample;
INSERT INTO #sample (column2,
column1)
--Inserts into the wanted columns, as they are specified
SELECT 2, 1;
INSERT INTO #sample
--Inserts into the wanted columns, as they are in the same order as the table
SELECT 3 AS Column1, 4 AS Column2;
SELECT *
FROM #sample;
DROP TABLE #sample;
When using an INSERT statement, it is considered best practice the specify the columns being inserted into in your INSERT clause. This helps others debug your code, as well as yourself in the future, as well as easily eliminates error like yours.

How to show only one row

I have this table structure and the sample data as well. I want to get only one row of the data. But instead it is giving me rows equal to it's child records.
--DROP TABLE [Detail];
--DROP TABLE [Master];
--CREATE TABLE [Master]
--(
--ID INT NOT NULL PRIMARY KEY,
--Code VARCHAR(25)
--);
--INSERT INTO [Master] VALUES (1, 'CASH');
--INSERT INTO [Master] VALUES (2, 'CASH');
--CREATE TABLE [Detail]
--(
--ID INT NOT NULL PRIMARY KEY,
--MasterID INT,
--DrAmount Numeric,
--CrAmount Numeric,
--CONSTRAINT FK_MASTER FOREIGN KEY (MasterID)
--REFERENCES [Master](ID)
--);
--INSERT INTO [Detail] VALUES (1, 1, '2200', NULL);
--INSERT INTO [Detail] VALUES (2, 1, NULL, '3200');
--INSERT INTO [Detail] VALUES (3, 1, '1000', NULL);
--INSERT INTO [Detail] VALUES (4, 2, NULL, '3200');
--INSERT INTO [Detail] VALUES (5, 2, '3200', NULL);
Here is the query and result:
SELECT [MASTER].[Code], [DETAIL].[MasterID], [DETAIL].[CrAmount]
FROM [MASTER], [DETAIL]
WHERE [MASTER].[ID] = [DETAIL].[MasterID]
Looks like you need GROUP BY and as #HoneyBadger suggests, it would be better to use the modern explicit join syntax - it is much more clear:
select m.code, d.masterid, sum(d.cramount) amount
from [master] m
join[detail] d on m.[id] = d.[masterid]
group by m.code, d.masterid
Result:
code masterid amount
CASH 1 3200
CASH 2 3200

Output inserted row from CTE

I have a CTE and I need to populate that CTE with the row that has been inserted.
I tried using temp table.
I am not sure how to create temp table within CTE and fill CTE.
This is what I have tried:
WITH RESULT AS
(
DECLARE #INSERTOUTPUT1 TABLE
(
BOOKID INT,
BOOKTITLE NVARCHAR(50),
MODIFIEDDATE DATETIME
);
-- INSERT NEW ROW INTO BOOKS TABLE
INSERT INTO BOOKS
OUTPUT INSERTED.* INTO #INSERTOUTPUT1
VALUES(101, 'ONE HUNDRED YEARS OF SOLITUDE', GETDATE());
SELECT * FROM #INSERTOUTPUT1
)
SELECT * FROM RESULT
Below is the schema for the table:
DROP TABLE dbo.Books;
CREATE TABLE dbo.Books
(
BookID int NOT NULL PRIMARY KEY,
BookTitle nvarchar(50) NOT NULL,
ModifiedDate datetime NOT NULL
);
you can't have the declare statement inside the CTE. It should be separate statement. Not sure what you wanted the CTE there for ? but there isn't a need for CTE
DECLARE #INSERTOUTPUT1 TABLE
(
BOOKID INT,
BOOKTITLE NVARCHAR(50),
MODIFIEDDATE DATETIME
);
INSERT INTO Books
OUTPUT INSERTED.* INTO #INSERTOUTPUT1
VALUES(101, 'ONE HUNDRED YEARS OF SOLITUDE', GETDATE());
select *
from #INSERTOUTPUT1

Column name or number of supplied values does not match table definition from SQL server

I get this error when I try to generate data for my database:
Column name or number of supplied values does not match table definition
This is the structure of my database:
Create database Newsagents;
USE Newsagents;
CREATE TABLE Client (
ClientID int NOT NULL,
Name char(30) NOT NULL,
City char(20) DEFAULT NULL,
Type VARCHAR(15) NOT NULL CHECK (type IN('Individual', 'Company'))
PRIMARY KEY (ClientID)
) ;
CREATE TABLE Product (
ProductNumber char(10) NOT NULL,
ProductName char(20) NOT NULL,
Price float NOT NULL,
isAvailable tinyint NOT NULL,
PRIMARY KEY (ProductNumber)
) ;
CREATE TABLE Sales (
ID INT NOT NULL ,
ClientID INT REFERENCES Client(ClientID),
ProductNumber CHAR(10) REFERENCES Product(ProductNumber),
Quantity INT NOT NULL,
Price FLOAT NOT NULL ,
Date TIMESTAMP NOT NULL,
PRIMARY KEY ( ID )
);
ALTER TABLE sales ADD CONSTRAINT d CHECK (Date > CURRENT_TIMESTAMP);
ALTER TABLE sales ADD CONSTRAINT i CHECK (Quantity > 0);
I than fill my database with some values for Client and Product and I want to generate Sales (using values from Client and Product). This is how I do it:
DECLARE #counter INT
DECLARE #quantity int
DECLARE #prodNum varchar(20)
SET #counter = 0
WHILE #counter < 10
BEGIN
SET #quantity = (select FLOOR(RAND()*100))
SET #prodNum = (select TOP 1 ProductNumber from Product Order by NEWID())
insert into Sales values(
(select TOP 1 ClientID from Client Order by NEWID()),
(select #prodNum),
(select #quantity),
((select #quantity)*(select TOP 1 Price from Product where ProductNumber = #prodNum)),
DEFAULT
)
SET #counter = #counter + 1
END
However I get the Column name or number of supplied values does not match table definition.
What am I doing wrong?
You're not inserting anything into ID column of Sales. You need to specify it in your query:
insert into Sales values(
SomeIDHere,
(select TOP 1 ClientID from Client Order by NEWID()),
(select #prodNum),
(select #quantity),
((select #quantity)*(select TOP 1 Price from Product where ProductNumber = #prodNum)),
DEFAULT
)
But maybe you want to have an autoincrement column for your ID?
CREATE TABLE Sales (
ID INT IDENTITY(1,1) NOT NULL ,
ClientID INT REFERENCES Client(ClientID),
ProductNumber CHAR(10) REFERENCES Product(ProductNumber),
Quantity INT NOT NULL,
Price FLOAT NOT NULL ,
Date TIMESTAMP NOT NULL,
PRIMARY KEY ( ID )
);
In this case, you will need to specify the columns when inserting into Sales
insert into Sales (ClientID, ProductNumber, Quantity, Price, [Date])
values(
(select TOP 1 ClientID from Client Order by NEWID()),
(select #prodNum),
(select #quantity),
((select #quantity)*(select TOP 1 Price from Product where ProductNumber = #prodNum)),
DEFAULT
)

Persisted computed column with subquery

I have something like this
create function Answers_Index(#id int, #questionID int)
returns int
as begin
return (select count([ID]) from [Answers] where [ID] < #id and [ID_Question] = #questionID)
end
go
create table Answers
(
[ID] int not null identity(1, 1),
[ID_Question] int not null,
[Text] nvarchar(100) not null,
[Index] as [dbo].[Answers_Index]([ID], [ID_Question]),
)
go
insert into Answers ([ID_Question], [Text]) values
(1, '1: first'),
(2, '2: first'),
(1, '1: second'),
(2, '2: second'),
(2, '2: third')
select * from [Answers]
Which works great, however it tends to slow down queries quite a bit. How can I make column Index persisted? I have tried following:
create table Answers
(
[ID] int not null identity(1, 1),
[ID_Question] int not null,
[Text] nvarchar(100) not null,
)
go
create function Answers_Index(#id int, #questionID int)
returns int
with schemabinding
as begin
return (select count([ID]) from [dbo].[Answers] where [ID] < #id and [ID_Question] = #questionID)
end
go
alter table Answers add [Index] as [dbo].[Answers_Index]([ID], [ID_Question]) persisted
go
insert into Answers ([ID_Question], [Text]) values
(1, '1: first'),
(2, '2: first'),
(1, '1: second'),
(2, '2: second'),
(2, '2: third')
select * from [Answers]
But that throws following error: Computed column 'Index' in table 'Answers' cannot be persisted because the column does user or system data access. Or should I just forget about it and use [Index] int not null default(0) and fill it in on insert trigger?
edit: thank you, final solution:
create trigger [TRG_Answers_Insert]
on [Answers]
for insert, update
as
update [Answers] set [Index] = (select count([ID]) from [Answers] where [ID] < a.[ID] and [ID_Question] = a.[ID_Question])
from [Answers] a
inner join [inserted] i on a.ID = i.ID
go
You could change the column to be a normal column and then update its value when you INSERT/UPDATE that row using a trigger.
create table Answers
(
[ID] int not null identity(1, 1),
[ID_Question] int not null,
[Text] nvarchar(100) not null,
[Index] Int null
)
CREATE TRIGGER trgAnswersIU
ON Answers
FOR INSERT,UPDATE
AS
DECLARE #id int
DECLARE #questionID int
SELECT #id = inserted.ID, #questionID = inserted.ID_question
UPDATE Answer a
SET Index = (select count([ID]) from [Answers] where [ID] < #id and [ID_Question] = #questionID)
WHERE a.ID = #id AND a.ID_question = #questionID
GO
NB* This is not fully correct as it wont work correctly on UPDATE as we wont have the "inserted" table to reference to get the ID and questionid. There is a way around this but i cant remember it right now :(
Checkout this for more info
Computed columns only store the formula of the calculation to perform. That is why it will be slower when querying the computed column from the table. If you want to persist the values to an actual table column, then you are correct about using a trigger.

Resources