How do I insert records from one table to another when some records don't fit a FK constraint? - sql

I have a source table that has 10,000 records that I want to insert into a destination table. The insert would also include an ID field. There is a third table with a foreign key relationship on the id field. How do I insert the records that have the correct ids for the FK and skip the ones that don't have the correct ids for the FK?
Set IDENTITY_INSERT Destination ON
GO
INSERT INTO [dbo].[Destination]
(
Id,
[Street1]
,[Street2]
,[City]
,[State]
,[PostalCode]
,[ContactId]
,[Institution_ID]
,[LegacyIDNumber]
)
SELECT DISTINCT
[Account Number],
[Address],
[Address 2],
[City],
Left([State],2),
[Postal Code],
[Account Number],
[Account Number],
[Account Number]
FROM [dbo].[Source]
GO
Set IDENTITY_INSERT Destination OFF
GO
The error I get is
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Destination_dbo.Contacts_ContactId". The conflict occurred in database "xxxx", table "dbo.Contacts", column 'Id'.
The statement has been terminated.

You could add an INNER JOIN to the Contacts table to ensure that the [Account Number] from the source matches the Id in the Contacts table. Anything that doesn't match would get filtered out which should keep you from getting the error about the FK constraint.
SELECT [Account Number],
[Address],
[Address 2],
[City],
Left([State],2),
[Postal Code],
[Account Number],
[Account Number],
[Account Number]
FROM [dbo].[Source]
INNER JOIN dbo.Contacts ON Contacts.id=Source.[Account Number]
There is probably a query hint or some other database trick that would allow you to pull this off more properly. But AFAIK the above should work.

Related

get values from master table based on fields in sqlserver

I want to retrieve the values from master table based on transaction table different col value,
My master table will have username,FirstName,lastName.
My transaction table will have Id,CreatedUser,UpdatedUser.
So I want query to get report from sql server ,get FirstName,lastName from master table for createruser and get FirstName,lastName from master table for updateuser.
Ex:
Master Table
User ID First Name Last Name
cer001 Ds CV
cer002 vb av
Transaction Table
id CreatedUser UdatedUser
2323 cer001 cer002
So Report should get the results like
Id CreatedUser UpdatedUSer
2323 Ds,CV Vb,av
it should be performance wise also good. Please help how to get this
You are looking for self join
select
t.Id, m.FirstName +','+m.LastName as CreatedUser,
mm.FirstName +','+mm.LastName as UpdatdUser
from Master m
inner join Transaction t on t.CreatedUser = m.[User ID]
inner join Master mm on mm.[User ID]= t.UpdatedUser
You should try left join from Transaction table to master table.
LEFT to take care of any NULL values
see working demo
Query will be
create table [Master] ([User ID] varchar(10), [First Name] varchar(10), [Last Name] varchar(10));
insert into [Master] values
('cer001','Ds','CV'),('cer002','vb','av')
create table [Transaction] (id int, CreatedUser varchar(10),UpdatedUser varchar(10))
insert into [Transaction] values
(2323, 'cer001','cer002');
select
ID=T.id,
CreatedUser= ISNULL (M1.[First Name],'')+ ','+ISNULL(M1.[Last Name],'' ),
UpdatedUser= ISNULL (M2.[First Name],'')+ ','+ISNULL(M2.[Last Name],'')
from
[Transaction] T
LEFT JOIN
[Master] M1
ON T.CreatedUser =M1.[User ID]
LEFT JOIN
[Master] M2
ON T.UpdatedUser =M2.[User ID]
Maybe you can try using sub query as below
Select
t.id,
(Select TOP 1 Rtrim(m.[First Name])+','+RTRIM(m.[Last Name]) from [Master] m where m.[User ID] = t.CreatedUser) as CreatedUser,
(Select TOP 1 Rtrim(m.[First Name])+','+RTRIM(m.[Last Name]) from [Master] m where m.[User ID] = t.UpdatedUser) as UpdatedUser
from [Transaction] t

Sum on columns with the same ID and different columns?

Using Microsoft SQL Server
I need to create a table that takes the User IDs from the temp table. Collects only those User IDs and then adds all of the Points for that individual user together.
Temp table which pulled all the most recent records for each User ID.
User ID | Points | Date Field
-----------------------------
1 1 10/31/2016
3 1 08/26/2016
Main
User ID | Points | Date Field | Other Field
--------------------------------------------
1 1 10/31/2016 N/A
1 2 10/25/2016 N/A
1 3 09/18/2016 N/A
2 1 08/17/2017 N/A
2 16 07/11/2017 N/A
3 1 08/27/2016 N/A
3 5 05/14/2016 N/A
So take temp table and match those User IDs to the main table. From that data you should only have collected those which match the record IDs in the temp table. From there the report will then be broken down to look like below. Which shows the most recent date, and a sum of all the points for that User ID.
User ID | Points | Date Field | Other Field
---------------------------------------------
1 6 10/31/2016 N/A
3 6 08/27/2016 N/A
I tried this SQL:
SELECT
a.[User ID], a.[Points], a.[Date Field], a.[Other Field1], a.[Other Field2], a.[Other Field3],
(CASE WHEN b.[User ID] = a.[User ID] THEN SUM(a.Points) END) AS [Total Points]
FROM
Main_Table a
INNER JOIN
#Temp b ON b.[User ID] = a.[User ID]
GROUP BY
a.[User ID], [Points], [Date Field], [Other Field1], [Other Field2], [Other Field3]
I get the total points column, but is not adding all the fields with identical User IDs together.
I just need to know where I am going wrong. I'm not the best with SQL and I'm still learning. Please understand I am trying my best to figure this out.
Thank you!
I imagine something like this is what you're looking for:
SELECT
b.[User ID]
, b.[Points]
, b.[Date Field]
, SUM(a.Points) [Total Points]
FROM
#Temp b
INNER JOIN Main_Table a ON b.[User ID] = a.[User ID]
GROUP BY
b.[User ID]
, b.[Points]
, b.[Date Field]
But then the question would be, which of the other fields from Main_Table do you want to keep?
I imagine possibly something like this:
SELECT
b.[User ID]
, b.[Points]
, b.[Date Field]
, m.[Other_Field] -- etc.
, SUM(a.Points) [Total Points]
FROM
#Temp b
INNER JOIN Main_Table m ON
b.[User ID] = m.[User ID]
AND b.[Date Field] = m.[Date Field]
INNER JOIN Main_Table a ON b.[User ID] = a.[User ID]
GROUP BY
b.[User ID]
, b.[Points]
, b.[Date Field]
, m.[Other_Field]
To get Sum, you have to group by fields that are non-changing. So User ID is the non-changing field you would want to SUM by. The group by is done to this non-aggregate field User ID. The problem with date and [Other Field] is that they are changing often. This defeats the group by. You have to query back to the original table to get the max date field if that's your intent. I'm not sure what [Other Field] are, but you have to take into account if they are different changing values.
SELECT dT.[User ID]
,dT.Points
,(SELECT MAX([Date Field])
FROM #main M2
WHERE M2.[User ID] = dT.[User ID]) AS [Date Field]
FROM (
SELECT M.[User ID]
,SUM(M.Points) AS Points
FROM #main M
WHERE [User ID] IN (SELECT [User ID] FROM #temp)
GROUP BY M.[User ID]
) AS dT
Creates output:
User ID Points Date Field
1 6 2016-10-31
3 6 2016-08-27
I assume this should help
--======================= CREATE TEMP TABLE==============================
CREATE TABLE #TEMP ([USER ID] INT,
POINTS INT,
[DATE FIELD] DATE)
--======================= VALUES ON THE TABLE ===============================
INSERT INTO #TEMP VALUES ( 1 ,1,'10-31-2016')
INSERT INTO #TEMP VALUES (3,1,'08-26-2016')
--======================== CREATE MAIN TABLE ============================
CREATE TABLE #MAIN ([USER ID] INT,
POINTS INT,
[DATE FIELD] DATE,
[OTHER FIELD] NVARCHAR(50))
--======================= VALUES ON MAIN TABLE===================
INSERT INTO #MAIN VALUES ( 1,1,'10-31-2016','N/A')
INSERT INTO #MAIN VALUES ( 1,2,'10-25-2016','N/A')
INSERT INTO #MAIN VALUES ( 1,3,'09-18-2016','N/A')
INSERT INTO #MAIN VALUES ( 2,1,'08-17-2017','N/A')
INSERT INTO #MAIN VALUES ( 2,16,'07-11-2017','N/A')
INSERT INTO #MAIN VALUES ( 3,1,'08-27-2016','N/A')
INSERT INTO #MAIN VALUES ( 3,5,'05-14-2016','N/A')
--=====GETTING SUM OF YOUR POINTS=====
SELECT T2.[USER ID],
SUM(T1.[POINTS]) AS [TOTAL POINT],
T2.[DATE FIELD],
T1.[OTHER FIELD]
FROM #MAIN AS T1
RIGHT JOIN #TEMP AS T2
ON T1.[USER ID] = T2.[USER ID]
GROUP BY T2.[USER ID],
T2.[DATE FIELD],
T1.[OTHER FIELD]
DROP TABLE #TEMP
DROP TABLE #MAIN

MS SQL Make a column of one table depend on other table values sum

I have a table "Order" like this, which contains information about the order.
Order ID | ... | Order Total
The order, however, consists of several items, there's also an "Item Order" table:
Item Order ID | Order ID | Item ID
And the "Item" table:
Item ID | Cost
Thus, Order <-> Item Order have a one-to-many relationship and Item Order <-> Item have many-to-one relationship.
Logically, the Order Total should depend on the cost of each item order in it, which would add the cost of the Item to the Total.
How do I set up the dependencies so, that the Order Total depends on all Item Orders corresponding to this order and sums up all needed Item costs? I guess it should also update upon adding new item orders to the order each time.
As indicated in the comments, I'd normally prefer not to store redundant, potentially incorrect data. If, however, there's a performance issue with calculating the total on the fly, the next best option is to get the system to do the calculations for you. This is an option if you use an indexed view.
Table setup:
create table dbo.Orders (
OrderID int not null,
/* NO Total here */
constraint PK_Orders PRIMARY KEY (OrderID)
)
go
create table dbo.Items (
ItemID int not null,
Cost decimal (19,4) not null,
constraint PK_Items PRIMARY KEY (ItemID)
)
go
create table dbo.OrderItems (
OrderItemID int not null,
OrderID int not null,
ItemID int not null,
/* I'd normally prefer Order/Item/Quantity and making Order/Item the PK */
constraint PK_OrderItems PRIMARY KEY (OrderItemID),
constraint FK_OrderItems_Orders FOREIGN KEY (OrderID) references Orders (OrderID),
constraint FK_OrderItems_Items FOREIGN KEY (ItemID) references Items (ItemID)
)
And now we can create the view:
create view dbo.OrderTotals
with schemabinding
as
select
OrderID,
COUNT_BIG(*) as LineCount, /* Required for indexed view with aggregate */
SUM(Cost) as OrderTotal
from
dbo.Items i
inner join
dbo.OrderItems o
on
i.ItemID = o.ItemID
group by
OrderID
go
create unique clustered index IX_OrderTotals on OrderTotals (OrderID)
Now, as you perform inserts, updates and deletes against the OrderItems or Items tables, this view's index (which actually contains all of the view data) is automatically updated for you.
This avoids any concerns about corner cases which you may miss if you use e.g. triggers to perform the updates manually.
You can create two triggers on Item Order and Item tables.
Trigger on Item table:
CREATE TRIGGER T_Item_Recalc_Order_Total
ON Item
FOR DELETE, INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE o
SET [Order Total] = c.[Cost]
FROM Order o
INNER JOIN
(
SELECT i_o.[Order_ID]
,SUM(Cost) AS Cost
FROM Item_Order i_o
INNER JOIN Item i
ON i.[Item ID] = i_o.[Item ID]
WHERE i_o.[Item ID] IN (
-- Sum up cost for changed rows only
SELECT COALESCE(d.[Item ID], i.[Item ID]) AS [Item ID]
FROM deleted d
FULL OUTER JOIN inserted i
ON d.[Item ID] = i.[Item ID]
WHERE d.[Item ID] IS NULL OR i.[Item ID] IS NULL OR d.[Cost] <> i.[Cost]
)
GROUP BY i_o.[Order_ID]
) c
ON o.[Order_ID] = c.[Order_ID]
END
Trigger on Item Order table:
CREATE TRIGGER T_Item_Order_Recalc_Order_Total
ON Item_Order
FOR DELETE, INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE o
SET [Order Total] = c.[Cost]
FROM Order o
INNER JOIN
(
SELECT i_o.[Order_ID]
,SUM(Cost) AS Cost
FROM Item_Order i_o
INNER JOIN Item i
ON i.[Item ID] = i_o.[Item ID]
WHERE i_o.[Order_ID] IN (
-- Sum up cost for changed rows only
SELECT deleted.[Order_ID]
UNION
SELECT inserted.[Order_ID]
)
GROUP BY i_o.[Order_ID]
) c
ON o.[Order_ID] = c.[Order_ID]
END

Update existing records table with ID of foreign key table

I have the following tables:
and
I recently added the [History Table ID] column and added a Foreign Key Reference to the History table. What has to happen is, the Value of the [History Table ID] column has to be updated with the ID value of the History table. I have already been able to get it right with 2 entries, the entries that have the ID of Diary table in the Description column of the History table. This query below accomplishes that:
Update Diary
SET [History Table ID] = History.ID
from History with (nolock)
WHERE [Lookup Table HA] = 7
and [Lookup Table HAS] = 19
and Description LIKE 'Diary item (%'
and PATINDEX('%)%', Description) > 13
and Dairy.ID = SUBSTRING(Description, 13, PATINDEX('%)%', Description)-13)
Is there any way that the rest can be updated at all? I just can't get my head around this.
Thanks in advance.
UPDATE:
Please see below for Updated Table shots: This is where my problem in Updating and joining lies:
As you have mentioned in comment, "IssueNumber will always be the same. GlobalId will also always be the same in both tables." So this will work fine:
UPDATE Diary
SET [History Table ID] = History.ID
FROM History WITH (NOLOCK)
WHERE Diary.[Global ID] = History.[Global ID]
AND Diary.IssueNumber = History.IssueNumber
Perhaps this isn't correct, but it looks like the Global Id (and Issue number) connects the two tables. If so, you can do:
Update Diary
SET [History Table ID] = History.ID
from History with (nolock)
WHERE Diary.[Global ID] = History.[Global ID] and
Diary.[Issue number] = History.[Issue number];
Otherwise, you need to figure out the logic that connects one record in History to a record in the Diary table.
EDIT:
Let me assume you want the biggest history id when there are multiple matches:
Update Diary
SET [History Table ID] = h.ID
from (select [Global ID], [Issue number], max(id) as maxid
from History with (nolock)
group by [Global ID], [Issue number]
) h
WHERE Diary.[Global ID] = h.[Global ID] and
Diary.[Issue number] = h.[Issue number];

SQL Server: select has too many columns for insert

I'm trying to run this query and I keep getting 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.
This error comes after I try doing INSERT INTO #tempTable SELECT.... It worked just fine when I had only 2 columns for each temp table, but now that I've added a third, it keeps giving me this error even though it appears to be selecting 3 columns to insert into the table with 3 columns.
The query overall is trying to get a few column values, (customer ids, contacts, and a salesman id) over different tables, but the problems have come from needing to root out duplicate customer IDs and making sure that only 1 salesman and 1 contact showing up for each customer. If it wasn't for the salemen id, the query would work perfectly. Here is what I have so far:
if object_id('tempdb..#tempTable') IS NOT NULL DROP TABLE #tempTable
if object_id('tempdb..#tempTable2') IS NOT NULL DROP TABLE #tempTable2
CREATE TABLE #tempTable(
CustomerID int,
ContactName nvarchar(50),
SalesmenID nvarchar(4)
)
CREATE TABLE #tempTable2(
CustomerID int,
ContactName nvarchar(50),
SalesmenID nvarchar(4)
)
INSERT INTO #tempTable
(CustomerID, ContactName,SalesmenID)
SELECT Customers.[Customer ID],Salesmen.[4 Letter ID],
CASE([Customer Contact].defaultprintonorder)
WHEN 0
THEN 'zzzzzzz_NOCONTACT'
ELSE
[Customer Contact].[Contact Name]
END as ContactName
From Customers
LEFT JOIN [Customer Contact] on Customers.[Customer ID]=[Customer Contact].[Customer ID]
Left Join [Customer Salesmen] On Customers.[Customer ID]=[Customer Salesmen].[Customer ID]
INNER JOIN Salesmen on [Customer Salesmen].[Salesman Name]=Salesmen.[Salesman Name]
WHERE Customers.[Customer ID] NOT IN(SELECT CustomerID FROM #tempTable)
GROUP BY Customers.[Customer ID], [Contact Name], DefaultPrintOnOrder
INSERT INTO #tempTable2 (CustomerID, ContactName,SalesmenID)
SELECT distinct CustomerID, '', SalesmenID FROM #tempTable
UPDATE #TempTable2 SET
#tempTable2.CustomerID=#tempTable.CustomerID,
#tempTable2.ContactName=#tempTable.ContactName
FROM
#TempTable2
INNER JOIN #TempTable ON #TempTable2.CustomerID=#TempTable.CustomerID
SELECT Salesmen.[4 Letter ID],
[Customers].[Customer ID],
[Customer Contact].[Contact Name]
FROM Customers
Right JOIN #TempTable2 ON
Customers.[Customer ID]=#TempTable2.CustomerID
Right JOIN [Customer Salesmen] ON
#TempTable2.CustomerID=[Customer Salesmen].[Customer ID]
INNER JOIN
[Salesmen] ON
[Customer Salesmen].[Salesman Name]=Salesmen.[Salesman Name]
LEFT JOIN
[Customer Contact] ON
#TempTable2.[CustomerID]=[Customer Contact].[Customer ID]
EDIT:
I added SalesmenID to the inserts, but now I'm getting this error message 3 times:
Invalid column name 'SalesmenID'.
It comes up once for the temptable insert and twice for the temptable2 insert
You're trying to store 3 values (Customers.[Customer ID], Salesmen.[4 Letter ID], ContactName) in 2 fields (CustomerID, ContactName)
INSERT INTO #tempTable
(CustomerID, ContactName)
SELECT Customers.[Customer ID],Salesmen.[4 Letter ID],
CASE([Customer Contact].defaultprintonorder)
WHEN 0
THEN 'zzzzzzz_NOCONTACT'
ELSE
[Customer Contact].[Contact Name]
END as ContactName
Are you sure the problem is where you indicated?
This insert has 2 destination columns and 3 select columns:
INSERT INTO #tempTable
(CustomerID, ContactName) -- 2 columns
SELECT Customers.[Customer ID], --column 1
Salesmen.[4 Letter ID], --column 2
CASE([Customer Contact].defaultprintonorder) -- column 3!
WHEN 0
THEN 'zzzzzzz_NOCONTACT'
ELSE
[Customer Contact].[Contact Name]
END as ContactName
From Customers...