Question on SQL Grouping - sql

I am trying to achieve the following without using sub query.
For a funding, I would like to select the latest Letter created date and the ‘earliest worklist created since letter created’ date for a funding.
FundingId Leter (1, 1/1/2009 )(1, 5/5/2009) (1, 8/8/2009) (2, 3/3/2009)
FundingId WorkList (1, 5/5/2009 ) (1, 9/9/2009) (1, 10/10/2009) (2, 2/2/2009)
Expected Result -
FundingId Leter WorkList (1, 8/8/2009, 9/9/2009)
I wrote a query as follows. It has a bug. It will omit those FundingId for which the minimum WorkList date is less than latest Letter date (even though it has another worklist with greater than letter created date).
CREATE TABLE #Funding(
[Funding_ID] [int] IDENTITY(1,1) NOT NULL,
[Funding_No] [int] NOT NULL,
CONSTRAINT [PK_Center_Center_ID] PRIMARY KEY NONCLUSTERED ([Funding_ID] ASC)
) ON [PRIMARY]
CREATE TABLE #Letter(
[Letter_ID] [int] IDENTITY(1,1) NOT NULL,
[Funding_ID] [int] NOT NULL,
[CreatedDt] [SMALLDATETIME],
CONSTRAINT [PK_Letter_Letter_ID] PRIMARY KEY NONCLUSTERED ([Letter_ID] ASC)
) ON [PRIMARY]
CREATE TABLE #WorkList(
[WorkList_ID] [int] IDENTITY(1,1) NOT NULL,
[Funding_ID] [int] NOT NULL,
[CreatedDt] [SMALLDATETIME],
CONSTRAINT [PK_WorkList_WorkList_ID] PRIMARY KEY NONCLUSTERED ([WorkList_ID] ASC)
) ON [PRIMARY]
SELECT F.Funding_ID,
Funding_No,
MAX (L.CreatedDt),
MIN(W.CreatedDt)
FROM #Funding F
INNER JOIN #Letter L ON L.Funding_ID = F.Funding_ID
LEFT OUTER JOIN #WorkList W ON W.Funding_ID = F.Funding_ID
GROUP BY F.Funding_ID,Funding_No
HAVING MIN(W.CreatedDt) > MAX (L.CreatedDt)
How can I write a correct query without using subquery?
Please help
Thanks
Lijo

using derived tables is as good as it gets:
OP's tables:
CREATE TABLE #Funding(
[Funding_ID] [int] IDENTITY(1,1) NOT NULL,
[Funding_No] [int] NOT NULL,
CONSTRAINT [PK_Center_Center_ID] PRIMARY KEY NONCLUSTERED ([Funding_ID] ASC)
) ON [PRIMARY]
CREATE TABLE #Letter(
[Letter_ID] [int] IDENTITY(1,1) NOT NULL,
[Funding_ID] [int] NOT NULL,
[CreatedDt] [SMALLDATETIME],
CONSTRAINT [PK_Letter_Letter_ID] PRIMARY KEY NONCLUSTERED ([Letter_ID] ASC)
) ON [PRIMARY]
CREATE TABLE #WorkList(
[WorkList_ID] [int] IDENTITY(1,1) NOT NULL,
[Funding_ID] [int] NOT NULL,
[CreatedDt] [SMALLDATETIME],
CONSTRAINT [PK_WorkList_WorkList_ID] PRIMARY KEY NONCLUSTERED ([WorkList_ID] ASC)
) ON [PRIMARY]
OP's sample data:
INSERT INTO #Funding (Funding_No) VALUES (1)
INSERT INTO #Funding (Funding_No) VALUES (2)
INSERT INTO #Letter (Funding_ID,CreatedDt) VALUES (1,'1/1/2009')
INSERT INTO #Letter (Funding_ID,CreatedDt) VALUES (1,'5/5/2009')
INSERT INTO #Letter (Funding_ID,CreatedDt) VALUES (1,'8/8/2009')
INSERT INTO #Letter (Funding_ID,CreatedDt) VALUES (2,'3/3/2009')
INSERT INTO #WorkList (Funding_ID,CreatedDt) VALUES (1, '5/5/2009')
INSERT INTO #WorkList (Funding_ID,CreatedDt) VALUES (1, '9/9/2009')
INSERT INTO #WorkList (Funding_ID,CreatedDt) VALUES (1, '10/10/2009')
INSERT INTO #WorkList (Funding_ID,CreatedDt) VALUES (2, '2/2/2009')
The table CREATEs look like TSQL, but no version was given, so a CTE could have been used as well. However, this uses derived tables:
SELECT
dt.Funding_ID,LCreatedDt,MIN(CreatedDt) AS WCreatedDt
FROM (SELECT
f.Funding_Id,l.LCreatedDt
FROM #Funding f
LEFT OUTER JOIN (SELECT
Funding_ID,MAX(CreatedDt) AS LCreatedDt
FROM #Letter
GROUP BY Funding_ID
) l ON f.Funding_ID=l.Funding_ID
) dt
LEFT OUTER JOIN #WorkList w ON dt.Funding_ID=w.Funding_ID
WHERE w.CreatedDt>dt.LCreatedDt
GROUP BY dt.Funding_ID,LCreatedDt
OUTPUT:
Funding_ID LCreatedDt WCreatedDt
----------- ----------------------- -----------------------
1 2009-08-08 00:00:00 2009-09-09 00:00:00
(1 row(s) affected)
to preempt any people claiming that my query uses a subquery, read this article first on
Subquery Fundamentals: http://msdn.microsoft.com/en-us/library/aa213252(SQL.80).aspx
A subquery is a SELECT query that
returns a single value and is nested
inside a SELECT, INSERT, UPDATE, or
DELETE statement, or inside another
subquery. A subquery can be used
anywhere an expression is allowed.

Your question is How can I write a correct query without using subquery?
But you're not using a sub-query... so you already have your answer.

Related

Bulk inert to parent children tables

I have the following three tables (each RobotPart has exactly one arm and one leg). I have a large number of {arm, leg} pairs need to be inserted. Any new combinations of arm and leg will create a new entry in RobotPart. Any existing combination will not be inserted. No updates are needed for either parent or children. I need preserve the identity columns. Any efficient ways to accomplish this in SQL?
CREATE TABLE [dbo].[Arm](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Model] [varchar](20) NULL,
CONSTRAINT [PK_dbo.Arm] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
) ON [PRIMARY]
CREATE TABLE [dbo].[Leg](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Model] [varchar](10) NULL,
CONSTRAINT [PK_dbo.Leg] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
) ON [PRIMARY]
CREATE TABLE [dbo].[RobotPart](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](20) NOT NULL,
[ArmId] [int] NOT NULL,
[LegId] [int] NOT NULL,
CONSTRAINT [PK_dbo.RobotPart] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[RobotPart] WITH CHECK ADD CONSTRAINT [FK_dbo.RobotPart_dbo.Arm_ArmId] FOREIGN KEY([ArmId])
REFERENCES [dbo].[Arm] ([Id])
GO
ALTER TABLE [dbo].[RobotPart] WITH CHECK ADD CONSTRAINT [FK_dbo.RobotPart_dbo.Leg_LegId] FOREIGN KEY([LegId])
REFERENCES [dbo].[Leg] ([Id])
GO
Step 1: Insert new arms and legs using a LEFT OUTER JOINs from your source table to the arms and legs table, respectively. (a separate insert statement for each table)
Step 2: Insert the new combinations using an inner join from your source table to arms and legs and a left outer join from the your source table to the RobotPart table.
Here is my initial try on it using MERGE. Not sure how this compare to Lmu92 proposed.
CREATE TYPE [dbo].[RobotPart_udtt] AS TABLE(
[Arm] [varchar](20) NOT NULL,
[Leg] [varchar](10) NOT NULL,
[Name] [varchar](20) NOT NULL
)
GO
CREATE PROCEDURE dbo.[prc_Component_Create]
#robotParts [RobotPart_udtt] READONLY
AS
BEGIN
SET NOCOUNT ON;
DECLARE #messageId INT
DECLARE #status INT
MERGE [Arm] AS TARGET
USING (
SELECT
tR.arm AS Model
FROM #robotParts AS tR
) AS SOURCE
ON TARGET.Model = SOURCE.Model
WHEN NOT MATCHED THEN
INSERT
(
Model
)
VALUES
(
SOURCE.Model
);
MERGE [Leg] AS TARGET
USING (
SELECT
tR.leg AS Model
FROM #robotParts AS tR
) AS SOURCE
ON TARGET.Model = SOURCE.Model
WHEN NOT MATCHED THEN
INSERT
(
Model
)
VALUES
(
SOURCE.Model
);
WITH NewParts (ArmId, LegId, Name)
AS
(
SELECT tA.Id
, tL.Id
, tR.Name
FROM #robotParts AS tR
INNER JOIN [Arm] AS tA
ON tR.Arm = tA.Model
INNER JOIN [Leg] AS tL
ON tR.Leg = tL.Model
)
INSERT INTO RobotPart (Name, ArmId, LegId)
SELECT tN.Name
, tN.ArmId
, tN.LegId
FROM NewParts AS tN
LEFT JOIN RobotPart AS tR
ON tR.ArmId = tN.ArmId
AND tR.LegId = tN.LegId
WHERE tR.ArmId IS NULL AND tR.LegId IS NULL
END

Why do i get this error when i do a SELECT..INSERT in SQL Server

Why do i get this error
The select list for the INSERT statement contains more items than the
insert list. The number of SELECT values must match the number of
INSERT columns.
When i run this query
INSERT INTO TempOutputOfGroupifySP
(MonthOfQuery,Associate,[NoOfClaims],[ActualNoOfLines],[AverageTATInDays],
[NoOfErrorsDiscovered],[VarianceinPercent],[NoOfClaimsAudited],[InternalQualInPercent],[ExternalQualInPercent]
)
SELECT (DATENAME(MONTH,[ClaimProcessedDate])) AS MonthOfQuery,
Temp.Associate AS Associate,
COUNT(*) AS [NoOfClaims],
SUM(NoOfLines) AS [ActualNoOfLines] ,
(SUM(DATEDIFF(dd,[ClaimReceivedDate],[ClaimProcessedDate]))/COUNT(*)) AS [AverageTATInDays],
A.[NoOfErrorsDiscovered] AS [NoOfErrorsDiscovered],
Temp.[MonthlyTarget] As [TargetNoOfLines],(Temp.[MonthlyTarget] - COUNT(*)) AS [VarianceInPercent],
B.[NoOfClaimsAudited] AS [NoOfClaimsAudited],
((A.[NoOfErrorsDiscovered]/NULLIF(B.[NoOfClaimsAudited],0))*100) AS [InternalQualInPercent],
NULL AS [ExternalQualInPercent]
FROM
(SELECT COUNT(*) AS [NoOfErrorsDiscovered] FROM TempTableForStatisticsOfAssociates T1 WHERE [TypeOfError] IS NOT NULL) AS A,
(SELECT COUNT(*) AS [NoOfClaimsAudited] FROM TempTableForStatisticsOfAssociates T2 WHERE Auditor IS NOT NULL) AS B,
TempTableForStatisticsOfAssociates Temp
GROUP BY DATENAME(MONTH,([ClaimProcessedDate])),
Temp.Associate,
A.[NoOfErrorsDiscovered],
Temp.[MonthlyTarget],
B.[NoOfClaimsAudited]
Strucuture of the target table is
CREATE TABLE [dbo].[TempOutputOfGroupifySP](
[MonthOfQuery] [nchar](10) NULL,
[Associate] [nvarchar](max) NULL,
[NoOfClaims] [int] NULL,
[ActualNoOfLines] [int] NULL,
[AverageTATInDays] [int] NULL,
[NoOfErrorsDiscovered] [int] NULL,
[VarianceInPercent] [float] NULL,
[NoOfClaimsAudited] [int] NULL,
[InternalQualInPercent] [float] NULL,
[ExternalQualInPercent] [float] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
Your INSERT INTO defines 10 colums for the insertion, however, your SELECT statement return 11 columns. You are either missing a column in your INSERT statement or returning one too many in your SELECT statement.
Comparing your table structure and your SELECT and INSERT the following line in your SELECT statement doesn't have a counterpart:
Temp.[MonthlyTarget] As [TargetNoOfLines]

Use trigger how to copy just inserted row

I'm using SQL Server 2008, and I have a trigger which I want to copy any rows in the My_Table into a archive History_Table table.
How to copy the entire old content of the table into the archive each time someone inserts a new row?
My table structure is
CREATE TABLE [dbo].[Stu_Table]
(
[Stu_Id] [int] NOT NULL,
[Stu_Name] [varchar] (15) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[Stu_Class] [int] NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Stu_Table] ADD CONSTRAINT [PK_Stu_Table] PRIMARY KEY CLUSTERED ([Stu_Id]) ON [PRIMARY]
GO
My archive table structure is
CREATE TABLE [dbo].[Stu_TableHistory]
(
[Stu_Id] [int] NOT NULL,
[Stu_Name] [varchar] (15) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[Stu_Class] [int] NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Stu_TableHistory] ADD CONSTRAINT [PK_Stu_TableHistory] PRIMARY KEY CLUSTERED ([Stu_Id]) ON [PRIMARY]
GO
My trigger syntax is
Create TRIGGER [dbo].[HistoryKeep]
ON [dbo].[Stu_Table]
INSTEAD OF INSERT
AS
BEGIN
IF((SELECT COUNT(*) FROM Stu_Table WHERE Stu_Id = (SELECT Stu_Id FROM INSERTED)) >= 1)
BEGIN
INSERT INTO dbo.Stu_TableHistory( Stu_Id, Stu_Name, Stu_Class )
SELECT Stu_Id, Stu_Name, Stu_Class FROM Stu_Table WHERE Stu_Id = (SELECT Stu_Id FROM INSERTED)
UPDATE x
SET x.Stu_Name = i.Stu_Name
FROM dbo.Stu_Table AS x
INNER JOIN inserted AS i ON i.Stu_Id = x.Stu_Id
END
ELSE
BEGIN
INSERT INTO dbo.Stu_Table( Stu_Id, Stu_Name, Stu_Class )
SELECT Stu_Id, Stu_Name, Stu_Class FROM INSERTED
END
END
In a word need help to transfer the old data from student table to archive table. My above trigger syntax can not satisfy me.
If have any query plz ask thanks in advance.
Instead of your current trigger, you should have something like:
Create TRIGGER [dbo].[HistoryKeep]
ON [dbo].[Stu_Table]
INSTEAD OF INSERT
AS
BEGIN
DECLARE #History table (
Action sysname not null,
STU_ID [int] NULL,
[Stu_Name] [varchar] (15) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[Stu_Class] [int] NULL
)
;MERGE INTO Stu_Table t
USING INSERTED i ON t.STU_ID = i.STU_ID
WHEN MATCHED THEN UPDATE SET STU_Name = i.STU_Name
WHEN NOT MATCHED THEN INSERT (STU_ID,STU_NAME,STU_CLASS) VALUES (i.STU_ID,i.STU_NAME,i.STU_CLASS)
OUTPUT $Action,deleted.stu_id,deleted.stu_name,deleted.stu_class INTO #History;
INSERT INTO stu_TableHistory (stu_id,stu_name,stu_class)
select stu_id,stu_name,stu_class from #History where Action='UPDATE'
END
Note, also, that you'll need to drop your current PK constraint on STU_TableHistory, since as soon as a row is updated more than once, there'll be two entries containing the same STU_ID.
As per my comment, this treats INSERTED as a table containing multiple rows throughout. So if Stu_Table contains a row for STU_ID 1, the following insert:
INSERT INTO STU_Table (STU_ID,STU_Name,STU_Class) VALUES
(1,'abc',null),
(2,'def',null)
will update the row for STU_ID 1, insert a row for STU_ID 2, and insert one row into stu_tableHistory (for STU_ID 1)

How to increment a primary key in an insert statement in SQL Server 2005

I need to write an insert statement into a table the columns looks like this
demandtypeid (PK, FK, int, not null)
characvalueid (PK, FK, int, not null)
percentage (int null)
lastuser (varchar(100), null)
lastedited (datetime, null)
Here is the INSERT statement. Notice the there is not values at the
value( , , 'Bob')
as I think that's where the auto-increment command should go
insert into tr_demandtypecharac(demandtypeID, characvalueid, lastuser)
values( , , 'Bob')
Please help with a simple little statement
I just want to know how to manually insert into this table
Here's my table structure:
CREATE TABLE [dbo].[tr_demandtypecharac](
[demandtypeid] [int] NOT NULL,
[characvalueid] [int] NOT NULL,
[percentage] [int] NULL,
[lastuser] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[lastedited] [datetime] NULL,
CONSTRAINT [PK_tr_dtc_pkey] PRIMARY KEY CLUSTERED
(
[demandtypeid] ASC,
[characvalueid] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[tr_demandtypecharac] WITH CHECK
ADD CONSTRAINT [FK_tr_dtc_cvid]
FOREIGN KEY([characvalueid]) REFERENCES [dbo].[tr_characvalue] ([characvalueid])
ALTER TABLE [dbo].[tr_demandtypecharac] WITH CHECK
ADD CONSTRAINT [FK_tr_dtc_dtid]
FOREIGN KEY([demandtypeid]) REFERENCES [dbo].[tr_demandtype] ([demandtypeid])
If you want an int column that is unique and autoincrementing, use the IDENTITY keyword:
CREATE TABLE new_employees
(
id_num int IDENTITY(1,1),
fname varchar (20),
minit char(1),
lname varchar(30)
)
Then when you insert into the table, do not insert anything for that column -- it will autoincrement itself.
Given the CREATE TABLE statement you posted, without auto-increment (aka identity) columns, you would insert providing all columns and values, like this:
insert into tr_demandtypecharac(
demandtypeid, characvalueid,
percentage, lastuser, lastedited)
values(2, 3, 80, 'Bob', '01/01/2012')
If, however, you do make them auto-increment by changing the CREATE TABLE to:
CREATE TABLE [dbo].[tr_demandtypecharac](
[demandtypeid] [int] NOT NULL IDENTITY(1,1),
[characvalueid] [int] NOT NULL IDENTITY(1,1),
[percentage] [int] NULL,
[lastuser] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[lastedited] [datetime] NULL,
CONSTRAINT [PK_tr_dtc_pkey] PRIMARY KEY CLUSTERED
(
[demandtypeid] ASC,
[characvalueid] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
)
Then you would insert providing all non-identity (non-autoincrement) columns like this:
insert into tr_demandtypecharac(
percentage, lastuser,
lastedited)
values(80, 'Bob', '01/01/2012')
However, it is not common to have more than one column as an identity (autoincrement) column, and generally, this column is the only PRIMARY KEY column.
If a column is an autoincement column (which is different than a primary key column) then you omit the column in your insert statement and it will be filled in.
INSERT INTO tr_demandtypecharac (lastuser) VALUES ('Bob')
I had a similar issue and needed to update a purchased database with a set of records. My solution was to find the highest key used so far, then use that as the base of my insert. The core of it was ROWNUMBER() OVER(ORDER BY PART_CODE).
The key is the "recnum" field in the inadjinf table. I determined that the highest current key was 675400 and updated my query to be:
insert into inadjinf (recnum, user_id, adj_type, adj_status, trans_date, part_code, lotqty, uom, cost_ctr, lot, location, to_cost_ctr, to_location, rec_status, to_part_grade, to_rec_status, remarks1, uom_conv)
select ROW_NUMBER() OVER(ORDER BY INVDET.PART_CODE) + 675400 as recnum, 'CHRSTR' as user_id, 'M' as adj_type, 'O' as adj_status, '2020-10-23' as trans_date, invdet.part_code, sum(lotqty) as lotqty, uom,
cost_ctr, lot, location, 'NS' as to_cost_ctr, '500A' as to_location, rec_status, 'Q' as to_part_grade, 'H' as to_rec_status, 'NS Scrap Request from MSobers 10/21/2020' as remarks1, '1' as uom_conv
from invdet
inner join partmstr on invdet.part_code = partmstr.part_code
where
invdet.part_code In
(
'86038',
'1271',
'VM-0021',
'CO-0107',
...
'FO-0391',
'FO-0376'
)
and lot not in (select lot from inadjinf where trans_date = '2020-10-23' and user_id = 'CHRSTR')
group by invdet.part_code, uom, cost_ctr, lot, location, rec_status
My output started with 675401 and went up from there. In the end, I updated the system's internal "next id field" table record.
You should not use int as primary keys... heres a article about it: http://techtrainedmonkey.com/2012/07/30/why-integers-are-lousy-primary-keys/
but if you do... set the field as identity and Sql Server will do it for you... check it out: http://msdn.microsoft.com/en-us/library/ms186775.aspx

sql server insert records from one table to another

how does one insert records from one table to another that has a unique index in the destination table without going through the insert and then removal of duplicates by deleting the index?
INSERT INTO forms(url,feedUrl, dateadded)
SELECT url, feedurl, dateadded
FROM Book3 T2
where not exists(select * from forms T1 where T1.url = T2.url;
T2.feedurl = T1.feedUrl and T2.dateadded =T1.dateadded)
Violation of UNIQUE KEY constraint 'IX_forms'. Cannot insert duplicate key in object 'dbo.forms'.
Table forms
CREATE TABLE [dbo].[forms](
[id] [int] IDENTITY(1,1) NOT NULL,
[url] [varchar](450) NULL,
[feedUrl] [varchar](450) NULL,
[dateadded] [datetime] NULL,
CONSTRAINT [PK_forms] PRIMARY KEY CLUSTERED
(
Table book3
CREATE TABLE [dbo].[Book3](
[url] [varchar](450) NULL,
[feedurl] [varchar](450) NULL,
[dateadded] [datetime] NULL
) ON [PRIMARY]
You may have duplicates in your results set. Does this query give you fewer records than the orginal select?
SELECT distinct url, feedurl, dateadded
FROM Book3 T2
where not exists(select * from forms T1 where T1.url = T2.url
T2.feedurl = T1.feedUrl and T2.dateadded =T1.dateadded)