Related
Sorry my English is very poor so I just can describe my problem very meager
I have used OPENROWSET to get a raw data table from an excel file as below
SELECT *
FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0',
'Excel 8.0;Database=C:\PP.xlsx',
'SELECT * FROM [Daily$]')
[enter image description here]
The problems is each column is a dynamically date which user who created the Excel file update it every day.
So I need a solution to select the column= current date (or any date).
Hopefully you can understand my point, below is my code:
Select
t1.*, temp.[13-Nov] as [Plan]
from
(select
case
when FinalMaterial = 'IA15_2020' then 'FADIL'
when FinalMaterial = 'ID2U_2020' then 'V8'
when FinalMaterial = 'ID1U_2019' then 'SUV'
when FinalMaterial = 'ID14_2019' then 'Sedan'
else FinalMaterial
end FinalMaterial,
case
when statusto in ('TP_40', 'TP_40B') then 'Paint'
when statusto = 'TP_20' then 'Body'
when statusto = 'TP_80' then 'OKTS'
when statusto = 'TP_F1' then 'F1 out'
else null
end Shop,
count(WorkOrder_NId) Actual
from
vLogicalStatusTrans_1138772611 lo
join
vWorkOrder_Siemens__1008010152 wo on wo.NId = lo.WorkOrder_NId
where
StatusTo in ('TP_40', 'TP_40B', 'TP_20', 'TP_80', 'TP_F1')
and dateadd(hh, 7, lo.TransitionTime) >= SMALLDATETIMEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), DAY(GETDATE()), 00, 00)
group by
wo.FinalMaterial, statusto) t1
left join
(select * from OPENROWSET('Microsoft.ACE.OLEDB.12.0','Excel 8.0;Database=C:\PP.xlsx','SELECT * FROM [Daily$]')) as temp on temp.Model = t1.FinalMaterial and temp.Shop = t1.Shop
where
t1.Shop = 'Paint'
Changing the select field list on the fly means constructing a "dynamic query" where your build the entire query in a string variable (nvarchar type) and pass that to a stored procedure like sp_executesql (documentation).
Sample data
create table exceldata
(
id int,
[14-nov] int,
[15-nov] int,
[16-nov] int
);
insert into exceldata (id, [14-nov], [15-nov], [16-nov]) values
(1, 114, 115, 116),
(2, 214, 215, 216);
Solution
declare #today date = getdate();
declare #stmt nvarchar(500) = 'select id, ['
+ replace(convert(nvarchar(6), #today, 106), ' ', '-')
+ '] from exceldata;'
select #stmt as Statement; -- validation
exec sp_executesql #stmt;
Result
For #today = '2020-11-14':
Statement
-----------------------------------
select id, [14-Nov] from exceldata;
id 14-Nov
--- ------
1 114
2 214
For #today = '2020-11-15':
Statement
-----------------------------------
select id, [15-Nov] from exceldata;
id 15-Nov
--- ------
1 115
2 215
Fiddle to see it in action (includes step by step date format construction).
I have a requirement, insert/update data from staging table stgTbl to another table T2. If exists update, no matter how many duplicates are there. Similarly, if not exists insert directly T2. Very Simple. Since the staging table stgTb1 is scheduled job everyday. Sometime I would get multiple duplicate rows as well. So because of duplicates, merge statement producing an error:
"The MERGE statement attempted to UPDATE or DELETE the same row more
than once. This happens when a target row matches more than one source
row".
I tried with checksum but still getting because i may done wrongly in checksum. There is no primary key in both the tables (i removed, because otherwise i am getting primary key constraint error). Based on columns (account and ref_key2) should go with insert/update. I have tried with if else with exists also but somehow this is also not working if in case there are 0 records in the target table T2. SQL masters could solve this issue. Appreciate their knowledge share.
CREATE OR ALTER Trigger [dbo].[tr_Merge_Personal_Expense] on [dbo].
[Personal_Expense_Staging]
AFTER INSERT
AS
BEGIN
DECLARE #SummaryOfChanges TABLE(Change nvarchar(20));
MERGE [dbo].[Personal_Expense] AS TARGET
USING (
SELECT *,
CHECKSUM(
ISNULL(CONVERT(nvarchar (3) ,[Client]),'0'),
ISNULL(CONVERT(nvarchar (50),[Text]),'0'),
ISNULL(CONVERT(nvarchar (10),[Cost_Center]),'0'),
ISNULL(CONVERT(nvarchar (2) ,[Posting_Period]),'0'),
ISNULL(CONVERT(nvarchar (10),[Profit]),'0') ,
ISNULL(CONVERT(nvarchar (2) ,[Document_Type]),'0'),
ISNULL(CONVERT(nvarchar (4) ,[Company_Code]),'0') ,
-- ISNULL(CONVERT(nvarchar (10),[Account]),'0'),
ISNULL(CONVERT(nvarchar (20),[Amount_In_Doc_Curr]),'0'),
ISNULL(CONVERT(nvarchar (5) ,[Document_Currency]),'0') ,
ISNULL(CONVERT(nvarchar (20),[Amount_In_local_Curr]),'0') ,
ISNULL(CONVERT(nvarchar (20),[Amount_In_Grp_Curr]),'0'),
ISNULL(CONVERT(nvarchar (25),[Group]),'0'),
[Posting_Date],
ISNULL(CONVERT(nvarchar (18), [Assignment]),'0'),
ISNULL(CONVERT(nvarchar (16), [Reference]),'0'),
ISNULL(CONVERT(nvarchar (20), [Reference_Key1]),'0') ,
--[Reference_Key2] [nvarchar](12) NOT NULL,
ISNULL(CONVERT(nvarchar (20), [Reference_Key3]),'0') ,
ISNULL(CONVERT(nvarchar (20), [Document_Number]),'0') ,
ISNULL(CONVERT(nvarchar (25), [Document_Header_Text]),'0')
) AS [HashChecksum]
FROM
[dbo].[Personal_Expense_Staging]
) AS SOURCE
ON (
TARGET.[Reference_Key2] = SOURCE.[Reference_Key2] AND
TARGET.[Account] = SOURCE.[Account]
)
----- Update
WHEN MATCHED AND ( TARGET.[HashChecksum] <> SOURCE.[HashChecksum] )
THEN
UPDATE SET
TARGET.[client] = SOURCE.[client],
TARGET.[text] = SOURCE.[text],
TARGET.[cost_center] = SOURCE.[cost_center],
TARGET.[posting_period] = SOURCE.[posting_period],
TARGET.[profit] = SOURCE.[profit],
TARGET.[document_type]= SOURCE.[document_type],
TARGET.[company_code] = SOURCE.[company_code],
TARGET.[Account] = SOURCE.[Account],
TARGET.[Amount_In_Doc_Curr] = SOURCE.[Amount_In_Doc_Curr],
TARGET.[document_currency] = SOURCE.[document_currency],
TARGET.[Amount_In_local_Curr] = SOURCE.[Amount_In_local_Curr],
TARGET.[Amount_In_Grp_Curr] = SOURCE.[Amount_In_Grp_Curr],
TARGET.[group] = SOURCE.[group],
TARGET.[posting_date] = SOURCE.[posting_date],
TARGET.[assignment] = SOURCE.[assignment],
TARGET.[reference] = SOURCE.[reference],
TARGET.[document_header_text] = SOURCE.[document_header_text],
TARGET.[Last_updated_DateTime] = GETDATE(),
TARGET.[HashChecksum] = SOURCE.[HashChecksum]
-- Insert
WHEN NOT MATCHED THEN
INSERT (
[Client],
[text],
[cost_center],
[posting_period],
[profit],
[document_type],
[company_code],
[Account],
[Amount_In_Doc_Curr],
[document_currency],
[Amount_In_local_Curr],
[Amount_In_Grp_Curr],
[group],
[posting_date],
[assignment],
[reference],
[reference_key2],
[reference_key3],
[document_number],
[document_header_text],
[Last_updated_DateTime],
[HashChecksum]
)
VALUES (
SOURCE.[client],
SOURCE.[text],
SOURCE.[cost_center],
SOURCE.[posting_period],
SOURCE.[profit],
SOURCE.[document_type],
SOURCE.[company_code],
SOURCE.[Account],
SOURCE.[Amount_In_Doc_Curr],
SOURCE.[document_currency],
SOURCE.[Amount_In_local_Curr],
SOURCE.[Amount_In_Grp_Curr],
SOURCE.[group],
SOURCE.[posting_date],
SOURCE.[assignment],
SOURCE.[reference],
SOURCE.[reference_key2],
SOURCE.[reference_key3],
SOURCE.[document_number],
SOURCE.[document_header_text],
GETDATE(),
SOURCE.[HashChecksum]
)
OUTPUT $action INTO #SummaryOfChanges;
DECLARE #RowsProcessed INT = 0;
SELECT
#RowsProcessed = ISNULL([INSERT],0) + ISNULL([UPDATE],0) + ISNULL([DELETE],0)
FROM (
SELECT COUNT(*) ChangeCount, Change as ChangeType
FROM #SummaryOfChanges
GROUP BY Change
)Main
PIVOT (
MAX(ChangeCount)
FOR ChangeType IN ([INSERT],[UPDATE],[DELETE])
)Pvt;
SELECT #RowsProcessed AS RowsProcessed;
CREATE OR ALTER TRIGGER [dbo].[tr_merge_personal_expense]
ON personal_expense_staging
AFTER INSERT AS
----- CASE 1: IF (ACCOUNT AND REF_KEY2) ARE MATCHED THEN UPDATE
IF EXISTS
(
SELECT
*
FROM dbo.personal_expense p
INNER JOIN inserted e
ON p.[account] = e.[account] AND p.[reference_key2] = e.
[reference_key2]
)
BEGIN
UPDATE target
SET
target.[document_number] = source.[document_number],
target.[client] = source.[client],
target.[text] = source.[text],
target.[cost_center] = source.[cost_center],
target.[posting_period] = source.[posting_period],
target.[profit] = source.[profit],
target.[document_type] = source.[document_type],
target.[company_code] = source.[company_code],
target.[amount_in_doc_curr] = source.[amount_in_doc_curr],
target.[document_currency] = source.[document_currency],
target.[amount_in_local_curr] = source.[amount_in_local_curr],
target.[amount_in_grp_curr] = source.[amount_in_grp_curr],
target.[group] = source.[group],
target.[posting_date] = source.[posting_date],
target.[assignment] = source.[assignment],
target.[reference] = source.[reference],
target.[reference_key1] = source.[reference_key1],
target.[reference_key3] = source.[reference_key3],
target.[document_header_text] = source.[document_header_text],
target.[last_updated_datetime] = GETDATE()
FROM personal_expense target
INNER JOIN inserted source
ON target.[account] = source.[account]
AND target.[reference_key2] = source.[reference_key2];
END;
------ CASE 2: IF (ACCOUNT AND REF_KEY2) ARE NOT MATCHED THEN INSERT
IF NOT EXISTS
(
SELECT
*
FROM dbo.personal_expense p
INNER JOIN inserted e
ON p.[account] = e.[account] AND p.[reference_key2] = e.
[reference_key2]
)
BEGIN
INSERT INTO dbo.personal_expense
(
[client],
[text],
[cost_center],
[posting_period],
[profit],
[document_type],
[company_code],
[account],
[amount_in_doc_curr],
[document_currency],
[amount_in_local_curr],
[amount_in_grp_curr],
[group],
[posting_date],
[assignment],
[reference],
[reference_key1],
[reference_key2],
[reference_key3],
[document_number],
[document_header_text],
[last_updated_datetime]
)
SELECT
source.[client],
source.[text],
source.[cost_center],
source.[posting_period],
source.[profit],
source.[document_type],
source.[company_code],
source.[account],
source.[amount_in_doc_curr],
source.[document_currency],
source.[amount_in_local_curr],
source.[amount_in_grp_curr],
source.[group],
source.[posting_date],
source.[assignment],
source.[reference],
source.[reference_key1],
source.[reference_key2],
source.[reference_key3],
source.[document_number],
source.[document_header_text],
GETDATE()
FROM inserted source;
END;
SO, you know that of duplicates, merge statement producing an error. Then simply remove the duplicates in your source clause, using DISTINCT:
SELECT DISTINCT *
FROM [dbo].[Personal_Expense_Staging]
I have a trigger which adds a log entry into a table upon a field change in another table. it works when one row is changed but errors when multiple rows re changed. Anyone out there able to explain what I have to do to get my trigger working also for multi row updates?
Many thanks,
Derek
Declare #PropertyID uniqueidentifier
Set #PropertyID = (Select CONVERT(VARCHAR( 36 ), ISNULL(i.[PropertyPK], d.[PropertyPK]))
FROM
INSERTED i
FULL OUTER JOIN DELETED d ON ( d.[PropertyPK] = i.[PropertyPK] )
WHERE
( d.[strManagingOfficeName] <> i.[strManagingOfficeName] ) OR
( d.[strManagingOfficeName] IS NULL AND i.[strManagingOfficeName] IS NOT NULL ) OR
( i.[strManagingOfficeName] IS NULL AND d.[strManagingOfficeName] IS NOT NULL ))
Declare #CompanyID uniqueidentifier
Set #CompanyID = (Select CompanyFK From Property Where PropertyPK = #PropertyID)
--Deleted Old Ones
Delete From TDSAPILog Where ObjectFK = #PropertyID And strObject = 'Branch Change'
--Insert New Log
INSERT dbo.TDSAPILog(TDSAPILogPK, ObjectFK, strObject, strStatus, CompanyFK, dteDateLogged)
SELECT
NewID(),
#PropertyID,
'Branch Change',
'Active',
#CompanyID ,
GetDate()
This error occur when you return more than 1 value from a query and save in a variable or compare with a value in where clause.
In your example I think the error occur at this line
SET #CompanyID = (SELECT CompanyFK FROM Property WHERE PropertyPK = #PropertyID)
To resolve the reported error just put "TOP 1" in your query. Example is shown here:
SET #CompanyID = (SELECT TOP 1 CompanyFK FROM Property WHERE PropertyPK = #PropertyID)
Subquery returned more than 1 value error may occur at the following scenarios:
SET #YouVariable = (SELECT ColumnID FROM yourTable WHERE Identity = #SomeValue)
-- if the above query return more than 1 value the same error will be occurred
-- to resolve this problem just put "TOP 1" before ColumnID
SELECT *
FROM OtherTable
WHERE OtherIdentity = ((SELECT ColumnID FROM yourTable
WHERE Identity = #SomeValue))
-- if the above query return more than 1 value the same error will be occurred
-- to resolve this problem just replace "= with IN()". Example give below
SELECT *
FROM OtherTable
WHERE OtherIdentity IN ((SELECT ColumnID FROM yourTable
WHERE Identity = #SomeValue))
Intellisense error:
Msg 156, Level 15, State 1, Procedure Location_CVT_Insert, Line 19 [Batch Start Line 2]
Incorrect syntax near the keyword 'ON'.
Stored procedure code:
DROP PROCEDURE IF EXISTS [dbo].[Location_CVT_Insert]
GO
CREATE PROCEDURE [dbo].[Location_CVT_Insert]
(#Location_NotificationJson NVARCHAR(MAX))
AS
BEGIN
MERGE INTO Location_CVT AS C
USING (SELECT
deviceID, lastSeen, locationMapHierarchy, locationCoordinateX,
locationCoordinateY, locationCoordinateUnit, geoCoordinateLat,
geoCoordinateLong, geoCoordinateUnit
FROM
OPENJSON(#Location_NotificationJson)
WITH
(deviceId nchar(17),
lastSeen varchar(128),
locationMapHierarchy nvarchar(256),
locationCoordinateX float,
locationCoordinateY float,
locationCoordinateUnit nvarchar(64),
geoCoordinateLat float,
geoCoordinateLong float,
geoCoordinateUnit nvarchar(64)) AS InputJSON) ON (C.deviceId = InputJSON.deviceId)
WHEN MATCHED THEN
UPDATE
SET C.deviceId = InputJSON.deviceId,
C.lastSeen = InputJSON.lastSeen,
C.locationMapHierarchy = InputJSON.locationMapHierarchy,
C.locationCoordinateX = InputJSON.locationCoordinateX,
C.locationCoordinateY = InputJSON.locationCoordinateY,
C.locationCoordinateUnit = InputJSON.locationCoordinateUnit,
C.geoCoordinateLat = InputJSON.geoCoordinateLat,
C.geoCoordinateLong = InputJSON.geoCoordinateLong,
C.geoCoordinateUnit = InputJSON.geoCoordinateUnit
WHEN NOT MATCHED THEN
INSERT (deviceId, lastSeen, locationMapHierarchy,
locationCoordinateX, locationCoordinateY,
locationCoordinateUnit, geoCoordinateLat,
geoCoordinateLong, geoCoordinateUnit)
VALUES (InputJSON.deviceId, InputJSON.lastSeen, InputJSON.locationMapHierarchy,
InputJSON.locationCoordinateX, InputJSON.locationCoordinateY,
InputJSON.locationCoordinateUnit, InputJSON.geoCoordinateLat,
InputJSON.geoCoordinateLong, InputJSON.geoCoordinateUnit);
END
It seems that the ON statement is the problem. I'd like to select from a JSON object (SQL Server 2016) and if there is a match in my Location_CVT table on Device ID, update this entry, else insert a new record.
I am attempting to do something similar to the following example.
MERGE INTO Person AS P
USING (
SELECT *
FROM OPENJSON(#json)
WITH (id int, firstName nvarchar(50), lastName nvarchar(50),
age int, dateOfBirth datetime2) InputJSON
ON (P.id = InputJSON.id)
WHEN MATCHED THEN
UPDATE SET P.firstName = InputJSON.firstName,
P.lastName = InputJSON.lastName,
P.age = InputJSON.age,
P.dateOfBirth = InputJSON.dateOfBirth
WHEN NOT MATCHED THEN
INSERT (firstName, lastName, age, dateOfBirth)
VALUES (InputJSON.firstName, InputJSON.lastName, InputJSON.age,
InputJSON.dateOfBirth);
Source: https://www.codeproject.com/Articles/1087995/Inserting-JSON-Text-into-SQL-Server-Table
If, when you have parentheses that span multiple lines, you ensure that your open and close parentheses have consistent indentation, the issue becomes more clear (at least it does to me).
MERGE INTO Location_CVT AS C
USING
( SELECT
deviceID, lastSeen, locationMapHierarchy, locationCoordinateX,
locationCoordinateY, locationCoordinateUnit, geoCoordinateLat,
geoCoordinateLong, geoCoordinateUnit
FROM
OPENJSON(#Location_NotificationJson)
WITH
( deviceId nchar(17),
lastSeen varchar(128),
locationMapHierarchy nvarchar(256),
locationCoordinateX float,
locationCoordinateY float,
locationCoordinateUnit nvarchar(64),
geoCoordinateLat float,
geoCoordinateLong float,
geoCoordinateUnit nvarchar(64)
) AS InputJSON
) ------------------------------------------------- ISSUE IS HERE
ON (C.deviceId = InputJSON.deviceId)
WHEN MATCHED THEN
UPDATE
SET C.deviceId = InputJSON.deviceId,
C.lastSeen = InputJSON.lastSeen,
C.locationMapHierarchy = InputJSON.locationMapHierarchy,
C.locationCoordinateX = InputJSON.locationCoordinateX,
C.locationCoordinateY = InputJSON.locationCoordinateY,
C.locationCoordinateUnit = InputJSON.locationCoordinateUnit,
C.geoCoordinateLat = InputJSON.geoCoordinateLat,
C.geoCoordinateLong = InputJSON.geoCoordinateLong,
C.geoCoordinateUnit = InputJSON.geoCoordinateUnit
As you may now be able to see, your subquery that you are using for the source has no alias. It should be:
MERGE INTO Location_CVT AS C
USING
( SELECT
deviceID, lastSeen, locationMapHierarchy, locationCoordinateX,
locationCoordinateY, locationCoordinateUnit, geoCoordinateLat,
geoCoordinateLong, geoCoordinateUnit
FROM
OPENJSON(#Location_NotificationJson)
WITH
( deviceId nchar(17),
lastSeen varchar(128),
locationMapHierarchy nvarchar(256),
locationCoordinateX float,
locationCoordinateY float,
locationCoordinateUnit nvarchar(64),
geoCoordinateLat float,
geoCoordinateLong float,
geoCoordinateUnit nvarchar(64)
) AS InputJSON
) AS InputJSON -- ALIAS ADDED HERE
ON (C.deviceId = InputJSON.deviceId)
WHEN MATCHED THEN
UPDATE
SET C.deviceId = InputJSON.deviceId,
C.lastSeen = InputJSON.lastSeen,
C.locationMapHierarchy = InputJSON.locationMapHierarchy,
C.locationCoordinateX = InputJSON.locationCoordinateX,
C.locationCoordinateY = InputJSON.locationCoordinateY,
C.locationCoordinateUnit = InputJSON.locationCoordinateUnit,
C.geoCoordinateLat = InputJSON.geoCoordinateLat,
C.geoCoordinateLong = InputJSON.geoCoordinateLong,
C.geoCoordinateUnit = InputJSON.geoCoordinateUnit
I have the following SQL Merge statement.
DECLARE #TmpTable TABLE (BusinessBaseID int, BatchID uniqueidentifier, SupplierID int, SupplierVenueID varchar(200), AddressID int,[Action] varchar(50))
DECLARE #noop int; -- needed for the NO-OP below
Declare #TestOp Varchar(max)
Set #TestOp = 't'
-- Insert into BusinessBase retrieving all inserted BusinessBaseIDs mappings via tmptable
-- Another SQL blck goes here to insert records into TEMP table
MERGE Business.Address AS t
USING (SELECT tmp.BusinessBaseID, tmp.BatchID, tmp.SupplierID, tmp.SupplierVenueID,
v.Name, v.AddressLine1, v.AddressLine2, v.City, v.County, v.PostalCode,
v.Latitude,
v.Longitude,
dbo.GetVenueId(v.AddressLine1, v.AddressLine2, v.City, v.County, v.PostalCode, 'GB', v.Latitude, v.Longitude) as VenueId
FROM #TmpTable as tmp INNER JOIN Supplier.VenueImport as v
ON tmp.BatchID = v.BatchID AND tmp.SupplierID = v.SupplierID AND tmp.SupplierVenueID = v.SupplierVenueID
WHERE (tmp.BatchID = 'D7F369F1-A66A-4440-8D4B-2F521F672916') AND (tmp.SupplierID = 17)
) AS s
ON S.VenueId >0
WHEN MATCHED THEN
UPDATE SET #TestOp = #TestOp + ':' +convert(varchar, S.VenueId)+'|'+Convert(varchar,t.AddressId) -- the NO-OP instead of update
WHEN NOT MATCHED BY TARGET THEN
INSERT (AddressLine1, AddressLine2, City, StateProvince,PostalCode,CountryCode,Lat,Long)
VALUES (AddressLine1, AddressLine2, City, County, PostalCode, 'GB', Latitude, Longitude)
OUTPUT s.BusinessBaseID, s.BatchID, s.SupplierID, s.SupplierVenueID,ISNULL(INSERTED.AddressID,deleted.addressId),$action INTO #TmpTable;
Select #TestOp;
Select #Temp where [Action] = 'Update'
Above query returning all the rows (except newly inserted records). Where as it suppose to return only 1 record as S.VenueId is greater than 0 for only one record.
dbo.GetVenueId is a function which returns an integer. It will be > 0 for existing records and -1 for not existing records.
Could somebody point me where I am doing wrong.
Thanks,
Naresh
Change the call dbo.GetVenueId to the following
CASE dbo.GetVenueId(v.AddressLine1, v.AddressLine2, v.City, v.County, v.PostalCode, 'GB', v.Latitude, v.Longitude) WHEN -1 THEN -1 ELSE 0 END as VenueId
So It will act well