There is table contain
ID Qty
----------
1 2
2 4
3 1
4 5
Now if i had to choose rows where sum of Qty equals to 10,
How can i do this ?
like 2+4+1 = 7
but if i add 5 then 12
so ignore 2, then
4+1+5 = 10
How can i achieve this ?
Edit:
I want any possible combination which sums up to gives value.
suppose 7 then any rows which sums up to 7
like wise if 8 then any rows sums up to 8
want row/rows having combination equals to given value.
The problem you want to solve is called the subset sum problem. Unfortunately, it is NP-complete.
This means that, whether you use SQL or any other language to solve it, you will only be able to solve very small instances of the problem, i.e. ones with only a few entries in the table. Otherwise, the runtime will become excessive, since it grows exponentially with the number of rows in the table. The reason for this is that there is essentially no better way of finding the solution than to try all possible combinations.
If an approximate solution is acceptable, there is a polynomial time algorithm, which is described on the Wikipedia page.
If you want it to be exactly always three numbers that add to 10, then this
SELECT
*
FROM
MyTable t1
JOIN
MyTable t2 ON t1.ID <> t2.ID
JOIN
MyTable t3 ON t1.ID <> t3.ID AND t2.ID <> t3.ID
WHERE
t1.Qty + t2.Qty + t3.Qty = 10
If you want 2 or 4 or 5 numbers then you can't really do it in SQL
Edit:
Updated to ignore by ID not Qty
And again: If you want 2 or 4 or 5 numbers then you can't really do it in SQL
When you are dealing with such task in SQL you need to go to the cursors approach.
Cursors let you perform row by row operations and that's what you need, for example:
You want to make groups of where the summed quantity is 10
This are the tasks
select all the table into a temp table #TBL_ALL
loop row by row and when the rows sum up 10, delete from the temp table and insert into a final temp table #TBL_FINAL
do this until the #TBL_ALL sum cant be performed
An example:
Test table:
/****** Object: Table [dbo].[tblExample] Script Date: 06/09/2011 11:25:27 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[tblExample](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Qty] [int] NOT NULL,
CONSTRAINT [PK_tblExample] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Test Data:
INSERT INTO tblExample SELECT 2;
INSERT INTO tblExample SELECT 4;
INSERT INTO tblExample SELECT 1;
INSERT INTO tblExample SELECT 5;
INSERT INTO tblExample SELECT 5;
INSERT INTO tblExample SELECT 11;
INSERT INTO tblExample SELECT 1;
INSERT INTO tblExample SELECT 2;
INSERT INTO tblExample SELECT 3;
INSERT INTO tblExample SELECT 4;
INSERT INTO tblExample SELECT 7;
INSERT INTO tblExample SELECT 9;
INSERT INTO tblExample SELECT 1;
INSERT INTO tblExample SELECT 2;
Store procedure:
http://pastebin.com/EFeZcKXf
Results:
Grouped table:
ids qty group
12 9 1
7 1 1
11 7 2
9 3 2
4 5 3
5 5 3
2 4 4
10 4 4
14 2 4
Numbers not used:
id qty
1 2
8 2
3 1
13 1
6 11
Hope it helps
SP for those who cant access PasteBin
CREATE PROCEDURE getGroups
(
#groupByQty int, -- grouping number
#numberRuns int -- how many loops
-- usage: getGroups 10, 10
)
AS
SET NOCOUNT ON;
-- declare all variables
DECLARE #rowId int,
#rowQty int,
#rowTotal int,
#groupId int,
#totalRuns int,
#continue bit
-- set up our final temporary table
CREATE TABLE #TBL_COUNT
(
ids NVARCHAR(4000),
qty int,
[group] int
)
-- initializate variable
SET #groupId = 1;
SET #continue = 1;
SET #totalRuns = 0;
SELECT Id, Qty INTO #TBL_ALL FROM tblExample ORDER BY Qty DESC;
WHILE #totalRuns <= #numberRuns
BEGIN
-- declare the cursor
DECLARE Product CURSOR FOR SELECT Id, Qty FROM #TBL_ALL ORDER BY Qty DESC;
OPEN Product;
FETCH Product INTO #rowId, #rowQty;
PRINT ' ';
PRINT '### Run: ' + CAST(#totalRuns AS nvarchar(10)) + ' #################################################################';
PRINT 'Grouping Table by ' + CAST(#groupByQty AS nvarchar(10)) + ' | group id = ' + CAST(#groupId AS nvarchar(10));
-- Retrieve and process the first row
SELECT Top 1 #rowId = Id, #rowQty = Qty FROM #TBL_ALL ORDER BY Qty DESC;
PRINT 'First Row: id = ' + CAST(#rowId AS nvarchar(10)) + ' | qty = ' + CAST(#rowQty AS nvarchar(10));
-- sum it up and see if we have #groupByQty
SELECT #rowTotal = ISNULL(SUM(qty),0) FROM #TBL_COUNT WHERE [group] = #groupId;
PRINT 'Current sum in #TBL_COUNT: #groupId = '+ CAST(#groupId AS nvarchar(10)) +' | #rowTotal = ' + CAST(#rowTotal AS nvarchar(10)) + ' | (#rowTotal + #rowQty) = ' + CAST((#rowTotal + #rowQty) AS nvarchar(10));
IF #rowQty > #groupByQty
BEGIN
PRINT ' x First row has an unused number';
END
ELSE
BEGIN
-- handle result
IF (#rowTotal + #rowQty) = #groupByQty
BEGIN
PRINT '+++ Current sum is ' + CAST(#groupByQty AS nvarchar(10)) + ' +++';
-- save number
INSERT INTO #TBL_COUNT SELECT #rowId, #rowQty, #groupId;
PRINT '### Inserted final # into #TBL_COUNT : id = ' + CAST(#rowId AS nvarchar(10)) + ' | qty = ' + CAST(#rowQty AS nvarchar(10)) + ' | group = ' + CAST(#groupId AS nvarchar(10));
-- remove from table as we use it already
DELETE FROM #TBL_ALL WHERE Id = #rowId;
-- we got 10, let's change our Groupping
SET #groupId = (#groupId + 1);
PRINT 'New group id: ' + CAST(#groupId AS nvarchar(10));
END
ELSE
BEGIN
IF (#rowTotal + #rowQty) < #groupByQty
BEGIN
PRINT '### Inserted into #TBL_COUNT : id = ' + CAST(#rowId AS nvarchar(10)) + ' | qty = ' + CAST(#rowQty AS nvarchar(10)) + ' | group = ' + CAST(#groupId AS nvarchar(10));
-- save number
INSERT INTO #TBL_COUNT SELECT #rowId, #rowQty, #groupId;
-- remove from table as we use it already
DELETE FROM #TBL_ALL WHERE Id = #rowId;
END
ELSE
BEGIN
PRINT ' x Unmatch number, will handle this latter';
END
END
END
-- start the main processing loop
WHILE ##Fetch_Status = 0
BEGIN
FETCH Product INTO #rowId, #rowQty;
PRINT '##Fetch_Status = ' + CAST(##Fetch_Status AS nvarchar(100));
IF ##Fetch_Status < 0
BEGIN
BREAK
END
-- we have the values of our row, let's use them
PRINT 'Fetched Row: id = ' + CAST(#rowId AS nvarchar(10)) + ' | qty = ' + CAST(#rowQty AS nvarchar(10));
-- sum it up and see if we have #groupByQty
SELECT #rowTotal = ISNULL(SUM(qty),0) FROM #TBL_COUNT WHERE [group] = #groupId;
PRINT 'Current sum in #TBL_COUNT: #groupId = '+ CAST(#groupId AS nvarchar(10)) +' | #rowTotal = ' + CAST(#rowTotal AS nvarchar(10)) + ' | (#rowTotal + #rowQty) = ' + CAST((#rowTotal + #rowQty) AS nvarchar(10));
-- handle result
IF (#rowTotal + #rowQty) = #groupByQty
BEGIN
PRINT '+++ Current sum is ' + CAST(#groupByQty AS nvarchar(10)) + ' +++';
-- save number
INSERT INTO #TBL_COUNT SELECT #rowId, #rowQty, #groupId;
PRINT '### Inserted final # into #TBL_COUNT : id = ' + CAST(#rowId AS nvarchar(10)) + ' | qty = ' + CAST(#rowQty AS nvarchar(10)) + ' | group = ' + CAST(#groupId AS nvarchar(10));
-- remove from table as we use it already
DELETE FROM #TBL_ALL WHERE Id = #rowId;
-- we got 10, let's change our Groupping
SET #groupId = (#groupId + 1);
PRINT 'New group id: ' + CAST(#groupId AS nvarchar(10));
-- start again
BREAK;
END
ELSE
BEGIN
IF (#rowTotal + #rowQty) < #groupByQty
BEGIN
PRINT '### Inserted into #TBL_COUNT : id = ' + CAST(#rowId AS nvarchar(10)) + ' | qty = ' + CAST(#rowQty AS nvarchar(10)) + ' | group = ' + CAST(#groupId AS nvarchar(10));
-- save number
INSERT INTO #TBL_COUNT SELECT #rowId, #rowQty, #groupId;
-- remove from table as we use it already
DELETE FROM #TBL_ALL WHERE Id = #rowId;
END
ELSE
BEGIN
PRINT ' x Unmatch number, will handle this latter';
END
END
END -- END WHILE ##Fetch_Status = 0
SET #totalRuns = #totalRuns + 1;
-- Close and dealocate
CLOSE Product;
DEALLOCATE Product;
END -- END WHILE totalRuns <= #numberRuns
-- let's sum our last group and remove it if it's not #groupByQty
SELECT #rowTotal = ISNULL(SUM(qty),0) FROM #TBL_COUNT WHERE [group] = #groupId;
IF #rowTotal <> #groupByQty
BEGIN
SET IDENTITY_INSERT #TBL_ALL ON
INSERT INTO #TBL_ALL (Id, Qty) SELECT Ids, Qty FROM #TBL_COUNT WHERE [group] = #groupId;
DELETE FROM #TBL_COUNT WHERE [group] = #groupId;
END
SET NOCOUNT OFF;
-- Show and Delete temp tables
SELECT * FROM #TBL_COUNT;
SELECT * FROM #TBL_ALL;
DROP TABLE #TBL_COUNT;
DROP TABLE #TBL_ALL;
P.S. I'm not a SQL Professional, so bear with me if I did something weird, and keep in mind that this is a performance waste, maybe someone can use the the Loop without Cursors, more in this page
If u add always 3 numbers its like gbn said, if not then u have to check every combination of ur rows which gives u 2 to the number_of_rows power cobinations and i dont see how can it be done on sql in one query. If u use loop in sql sure its possible but u should find some good algorithm to finish this task.
Related
I'm searching but i can't find a solution for my problem.
I've a table (say T1) with this data:
SampleCode
Name
Content
#
1
A#
#
2
B#
#
3
C#
#
1
A#
#
2
B#
#
3
C#
So i need a select query resulting in :
Column 1
Column 2
Column 3
SampleCode
#
#
Name.1
A#
A#
Name.2
B#
B#
Name.3
C#
C#
Does anyone has an hint?
Thanks
Fabio
Check out the 2 options in this article: https://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns/
If using TSQL, you could use a TRANSPOSE statement. Or you can create the result set using cursors/loops and dynamic SQL.
Fully Dynamic Solution
I can't believe I hammered this out... maybe I did it just to see if I could. It's long and complicated and uses several loops. I think you should re-evaluate what you're trying to do. This can't be run in a view. You would have to put into a function or stored procedure. You may want to evaluate the temp table names to see if they are compatible with your environment. Also, since the temp table name are not unique, you cannot run multiple instances; if you want to run multiple instances, you would have to add a unique identifier to a column in the temp tables or to temp table names. This is fully dynamic... you don't have to know how many columns you need ahead of time.
--**************************************************
--Header area... sample data and temp tables.
--**************************************************
--Clean up sample and result tables if they exist.
IF OBJECT_ID(N'tempdb..#T1') IS NOT NULL
DROP TABLE #T1;
IF OBJECT_ID(N'tempdb..#ResultsTemp') IS NOT NULL
DROP TABLE #ResultsTemp;
IF OBJECT_ID(N'tempdb..#codeTable') IS NOT NULL
DROP TABLE #codeTable;
--For Debugging.
DECLARE #debug int = 1; --0=no debug, 1=show debug messages, do not exec all SQL statements.
--Table var to hold sample data.
CREATE TABLE #T1 (
SampleCode nvarchar(50),
[Name] nvarchar(50),
Content nvarchar(50),
row_num int
);
--Load Sample Data.
INSERT INTO #T1 (SampleCode, [Name], Content)
VALUES
('#', '1', 'A#')
, ('#', '2', 'B#')
, ('#', '3', 'C#')
, ('#', '1', 'A#')
, ('#', '2', 'B#')
, ('#', '3', 'C#')
;
--**********END HEADER**********
--Number the rows so we can loop over them.
UPDATE #T1
SET row_num = newT1.row_num
FROM
(
SELECT t12.SampleCode
, t12.[Name]
, T12.Content
, ROW_NUMBER()OVER(ORDER BY SampleCode, [Name], Content) as row_num
FROM #T1 as t12
) AS newT1
WHERE #T1.SampleCode = newT1.SampleCode
AND #T1.[Name] = newT1.[Name]
AND #T1.Content = newT1.Content
;
SELECT * FROM #T1; --Debugging... just show the contents of #T1 after adding row_num
--Create temp table to load results.
CREATE TABLE #ResultsTemp (
Column1 nvarchar(50)
);
--Create some variable to track looping (without a cursor).
DECLARE #loopCount int = 1;
DECLARE #maxLoops int = (SELECT COUNT(DISTINCT SampleCode) FROM #T1);
DECLARE #sql nvarchar(512);
--Add columns to #ResultsTable dynamically
WHILE (#loopCount <= (#maxLoops))
BEGIN
SET #sql = 'ALTER TABLE #ResultsTemp ADD ' + QUOTENAME('Column' + CAST((#loopCount + 1) as nvarchar)) + ' nvarchar(50) NULL';
IF (#debug = 1) PRINT #sql;
EXEC (#sql);
SET #loopCount = #loopCount + 1;
END
--************************************************************
--SECTION FOR INSERTING FIRST ROW for "SampleCode"
--************************************************************
--Vars for tracking the SampleCode variations processed.
DECLARE #sampleLoop int = 1;
DECLARE #sampleCount int = (SELECT COUNT(DISTINCT SampleCode) FROM #T1);
CREATE TABLE #codeTable (
SampleCode nvarchar(50)
, row_num int
);
--Create a list of unique SampleCodes to loop over.
INSERT INTO #codeTable (SampleCode)
SELECT DISTINCT
SampleCode
FROM #T1;
UPDATE #codeTable
SET row_num = newCT.row_num
FROM
(
SELECT ct2.SampleCode
, ROW_NUMBER()OVER(ORDER BY SampleCode) as row_num
FROM #codeTable as ct2
) AS newCT
WHERE #codeTable.SampleCode = newCT.SampleCode
;
SET #sql = 'INSERT INTO #ResultsTemp (Column1) SELECT ''SampleCode''';
IF (#debug = 1) PRINT #sql;
EXEC (#sql);
WHILE (#sampleLoop <= #sampleCount)
BEGIN
SET #sql = 'UPDATE #ResultsTemp SET Column' + CAST(#SampleLoop + 1 as nvarchar) + '=(SELECT TOP 1 SampleCode FROM #codeTable WHERE row_num = ' + CAST(#sampleLoop as nvarchar) + ');';
IF (#debug = 1) PRINT #sql;
EXEC (#sql);
SET #sampleLoop = #sampleLoop + 1;
END
IF (#debug = 1) SELECT * FROM #ResultsTemp;
--**********END SECTION**********
--**************************************************
--SECTION FOR INSERTING THE REST OF THE CONTENT DATA
--**************************************************
--Vars for tracking number of rows processed from T1.
SET #loopCount = 1;
SET #maxLoops = (SELECT MAX(row_num) FROM #T1);
--Loop over each row in T1.
WHILE (#loopCount <= #maxLoops)
BEGIN
--Create a name for this row.
DECLARE #nameRaw nvarchar(50) = (SELECT TOP 1 [Name] FROM #T1 WHERE row_num = #loopCount);
DECLARE #codeNum nvarchar(50) = (
SELECT TOP 1 ct.row_num
FROM #T1 as t
INNER JOIN #codeTable as ct
ON ct.SampleCode = t.SampleCode
WHERE t.row_num = #loopCount);
DECLARE #name nvarchar(50) = 'Name.' + #nameRaw;
--First insert a row for this Name... if not already in the list.
SET #sql = 'INSERT INTO #ResultsTemp (Column1)
SELECT ''Name.'' + t.[Name]
FROM #T1 as t
LEFT OUTER JOIN #ResultsTemp as rt
ON rt.Column1 = ''' + #name + '''
WHERE t.row_num = ' + CAST(#loopCount as nvarchar) + '
AND rt.Column1 IS NULL;';
IF (#debug = 1) PRINT #sql;
EXEC (#sql);
--Update this Name row with the "content".
SET #sql = 'UPDATE rt
SET Column' + CAST(#codeNum + 1 as nvarchar) + '=t.Content
FROM #ResultsTemp as rt
INNER JOIN #T1 as t
ON t.row_num = ' + CAST(#loopCount as nvarchar) + '
AND t.[Name] = ''' + #nameRaw + '''
WHERE rt.Column1 = ''' + #name + ''';';
IF (#debug = 1) PRINT #sql;
EXEC (#sql);
SET #loopCount = #loopCount + 1;
END
--Show everything in the temp Results table.
SELECT *
FROM #ResultsTemp;
Result Set:
Static Select Solution
This next option is not dynamic. You have to know how many columns you need and then you can copy the "Column3" code to any new columns. You have to change the column name and update the "Select TOP 1" statement as commented for each new column you would copy.
WITH CodeTable AS (
SELECT DISTINCT t.SampleCode
FROM #T1 as t
)
SELECT DISTINCT
'Name.' + [Name]
, (
SELECT TOP 1 Content
FROM #T1 as t2
WHERE t2.SampleCode = (
SELECT TOP 1 SampleCode
FROM CodeTable as ct
ORDER BY SampleCode
)
AND t2.[Name] = t.[Name]
) as Column2
, (
SELECT TOP 1 Content
FROM #T1 as t2
WHERE t2.SampleCode = (
SELECT TOP 1 SampleCode
FROM CodeTable as ct
WHERE ct.SampleCode NOT IN (
SELECT TOP 1 SampleCode --Update the TOP 1 to be TOP [number of previous columns]
FROM CodeTable as ct2
ORDER BY ct2.SampleCode
)
ORDER BY ct.SampleCode
)
AND t2.[Name] = t.[Name]
) as Column3
FROM #T1 as t
Static PivotTable Solution
This solution is from the link I posted. Again, you have to know the number of columns you need and it doesn't have generic column names like you specify. But, it is another solution if you reconsider your output.
--Use PivotTable.
SELECT *
FROM
(
SELECT [Name], Content
FROM #T1
) AS SourceTable PIVOT(MAX(Content) FOR [Content] IN([A#],
[A#]
)) AS PivotTable;
Pivot table can solve your prooblem:
DECLARE #T TABLE (SampleCode sysname, Name sysname, Content sysname)
INSERT #T (SampleCode, Name, Content)
VALUES
('#', '1', 'A#'),
('#', '2', 'B#'),
('#', '3', 'C#'),
('#', '1', 'A#'),
('#', '2', 'B#'),
('#', '3', 'C#')
SELECT CONCAT('Name.', PIVOTTABLE.Name), [#], [#]
FROM
(
SELECT * FROM #T AS t
) AS SOURCE
PIVOT (
MAX(Content)
FOR SampleCode IN ([#], [#])
) AS PIVOTTABLE
Of course this is the solution for what you provided in the question.
When the columns are too many to manually write down then use Dynamic query and the only parts needed to be generate is the values in FOR expression and the same value in SELECT
I have a stored procedure which is selecting some 1-n relations and related data to the referenced column as XML data.
The purpose is to return a record and it's 1-n relations as ONE record with extra data columns, this is done using adding these related data as XML.
Reference table: (TABLE A)
ID NAME VALUE
---------------------
1 Sepehr 1000
2 Sarah 1001
Related table: (TABLE B)
ID Value FK_Value ORDER TITLE
-------------------------------------
1 A 1000 1 t1
2 B 1000 2 t2
3 C 1000 3 t3
I want to get this output:
ID NAME FK_Value Attribs
-----------------------------------------------------
1 Sepehr 1000 <XML><ID>1</ID><ID>2</ID><ID>3</ID></XML>
2 Sarah 1001 null
Actually I was hoping to create a view to do this, but I couldn't and someone told me its not possible using views.
Finally this is the stored procedure I have written - is this a correct approach or are there any other ways?
DECLARE #T1 table (A_ID int,Attribs XML)
DECLARE db_cursorLegendRowsValues CURSOR FOR
SELECT ID, VALUE
FROM A
OPEN db_cursorLegendRowsValues
FETCH NEXT FROM db_cursorLegendRowsValues INTO #loop_ID, #loop_VALUE
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE db_cursorInternal CURSOR FOR
SELECT TITLE, ORDER
FROM B
WHERE FK_Value = #loop_VALUE
OPEN db_cursorInternal
FETCH NEXT FROM db_cursorInternal INTO #tmpTitle, #ORDER
WHILE ##FETCH_STATUS = 0
BEGIN
SET #querySelect = #querySelect + ', MAX(CASE WHEN order = ' + cast(#ORDER as nvarchar(max)) + ' THEN value END) AS [' +REPLACE (#tmpTitle,' ','_') + '] '
FETCH NEXT FROM db_cursorInternal INTO #tmpTitle, #ORDER
END
CLOSE db_cursorInternal
DEALLOCATE db_cursorInternal
SET #query =
' SELECT ' + cast(#loop_ID as nvarchar(max)) +',(
SELECT A.Value,
'
SET #query = #query + STUFF(#querySelect,1,1,'') + ' FROM A
WHERE [A.Value] = ' + cast(#loop_VALUE as nvarchar(max)) + '
FOR XML RAW (''Item''), root (''Items'') , ELEMENTS XSINIL )'
SET #querySelect = ''
--PRINT(#query)
INSERT into #T1 execute (#query )
FETCH NEXT FROM db_cursorLegendRowsValues INTO #loop_ID, #loop_VALUE
END
CLOSE db_cursorLegendRowsValues
DEALLOCATE db_cursorLegendRowsValues
SELECT * FROM #T1
The whole lot can be condensed to a few lines, no cursors needed at all. This wil be useable in a VIEW:
DECLARE #tblA TABLE(ID INT IDENTITY,NAME VARCHAR(100),VALUE INT);
INSERT INTO #tblA VALUES
('Sepehr',1000)
,('Sarah',1001);
DECLARE #tblB TABLE(ID INT IDENTITY,Value VARCHAR(100),FK_Value INT,[ORDER] INT,TITLE VARCHAR(100));
INSERT INTO #tblB VALUES
('A',1000,1,'t1')
,('B',1000,2,'t2')
,('C',1000,3,'t3');
SELECT a.*
,(SELECT ID FROM #tblB AS b WHERE b.FK_Value=a.VALUE FOR XML PATH(''),ROOT('XML'),TYPE) AS Attribs
FROM #tblA AS a
The result
ID NAME VALUE Attribs
1 Sepehr 1000 <XML><ID>1</ID><ID>2</ID><ID>3</ID></XML>
2 Sarah 1001 NULL
It is possible to do this with just one query - you can use a subquery with your select as follows:
select id
, name
, value as fk_value
, (select id from #table_b b
where a.value = b.fk_value
for xml path (''), root ('xml'))
from #table_a a
I restored a SQL 2k5 Database to a SQL 2014 server (Note this is a SQL Web Edition instance) and it does not seem to execute at all.
CREATE TRIGGER [dbo].[tInsertTransactionsSnapshot]
ON [GDL_TNA].[dbo].[TRANSACTIONS]
AFTER INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #FirstRecord int
DECLARE #LastRecord int
DECLARE #MSTSQ int
DECLARE #EventDate varchar(50)
SET #FirstRecord = (SELECT TOP 1 T_ID FROM INSERTED ORDER BY T_ID ASC)
SET #LastRecord = (SELECT TOP 1 T_ID FROM INSERTED ORDER BY T_ID DESC)
insert into GDL_TNA.dbo.dbg(Dbgfirst,dbgLast) VALUES (#FirstRecord,#LastRecord)
WHILE #FirstRecord <= #LastRecord
BEGIN
SET #MSTSQ = (SELECT MST_SQ FROM INSERTED WHERE T_ID = #FirstRecord)
SET #EventDate = (SELECT T_Date FROM INSERTED WHERE T_ID = #FirstRecord)
IF EXISTS(SELECT NULL FROM GDL_TNA.dbo.SNAPSHOTs WHERE MST_SQ = #MSTSQ AND SS_EVENT_DATE = #EventDate AND SS_ACTIVE = 1 AND SS_TYPE = 'TRANSACTION')
BEGIN
UPDATE GDL_TNA.dbo.SNAPSHOTs SET SS_ACTIVE = 0 WHERE MST_SQ = #MSTSQ AND SS_EVENT_DATE = #EventDate AND SS_ACTIVE = 1 AND SS_TYPE = 'TRANSACTION'
END
INSERT INTO GDL_TNA.dbo.SNAPSHOTs
(
[SS_TYPE]
,[SS_EVENT]
,[MST_SQ]
,[MD_ACTIVE]
,[MST_FIRST_NAME]
,[MST_LAST_NAME]
,[MD_SHIFTSTART]
,[MD_SHIFTEND]
,[COMP_ID]
,[DEPT_ID]
,[FUNC_ID]
,[MST_PREVIOUS_FIRST_NAME]
,[MST_PREVIOUS_LAST_NAME]
,[MST_RATE_NORMAL]
,[MST_RATE_TIMEANDHALF]
,[MST_RATE_DOUBLE]
,[MST_RATE_SPECIAL]
,[SS_EVENT_ID]
,[SS_EVENT_DATE]
,[SS_EVENT_START]
,[SS_EVENT_END]
,[SS_EVENT_APPROVED]
,[SS_EVENT_RATETYPE]
)
SELECT
'TRANSACTION'
,'INSERT'
,MST_SQ
,MD_ACTIVE
,MST_FIRST_NAME
,MST_LAST_NAME
,MD_SHIFTSTART
,MD_SHIFTEND
,COMP_ID
,DEPT_ID
,FUNC_ID
,MST_PREVIOUS_FIRST_NAME
,MST_PREVIOUS_LAST_NAME
,MST_RATE_NORMAL
,MST_RATE_TIMEANDHALF
,MST_RATE_DOUBLE
,MST_RATE_SPECIAL
,(
SELECT T_ID
FROM INSERTED
WHERE T_ID = #FirstRecord
)
,(
SELECT T_Date
FROM INSERTED
WHERE T_ID = #FirstRecord
)
,ISNULL((
SELECT
CASE
WHEN len(T_TIMEIN) = 6 THEN left(T_TIMEIN,2) + ':' + right(left(T_TIMEIN,4),2) + ':' + right(T_TIMEIN,2) + '.000'
WHEN len(T_TIMEIN) < 6 THEN '0'+left(T_TIMEIN,1) + ':' + right(left(T_TIMEIN,3),2) + ':' + right(T_TIMEIN,2) + '.000'
END
FROM INSERTED
WHERE T_ID = #FirstRecord
),'00:00:00.000')
,ISNULL((
SELECT
CASE
WHEN len(T_TIMEOUT) = 6 THEN left(T_TIMEOUT,2) + ':' + right(left(T_TIMEOUT,4),2) + ':' + right(T_TIMEOUT,2) + '.000'
WHEN len(T_TIMEOUT) < 6 THEN '0'+left(T_TIMEOUT,1) + ':' + right(left(T_TIMEOUT,3),2) + ':' + right(T_TIMEOUT,2) + '.000'
END
FROM INSERTED
WHERE T_ID = #FirstRecord
),'00:00:00.000')
,1
,1
FROM GDL_TNA.dbo.MASTER_DETAILS
WHERE MST_SQ = #MSTSQ
SET #FirstRecord = #FirstRecord + 1
END
END
Steps I have Taken
Check Trigger Status: SELECT name, is_disabled FROM sys.triggers (All Enabled)
Drop & Recreate Triggers on 2014 instance
Ran a Recompile of table with sp_recompile
Any advice would be greatly appreciated.
In my database, my column consists of 40 same records named jee_phy_2 . I want to increment the number by 1 like jee_phy_2 to jee_phy_40. Can anybody tell me
DECLARE #q_id NVARCHAR(50),#txt bigint,#final nvarchar(50)
begin
SET #q_id='JEE_PHY_1'
UPDATE oems_question7 SET q_id=#q_id+1 WHERE id between 1 and 40
end
Is this what are you looking for ?
UPDATE x
SET x.q_id = 'jee_phy_' + cast(x.newNum as nvarchar(10))
FROM (
SELECT q_id, ROW_NUMBER() OVER (ORDER BY [q_id]) AS newNum
FROM oems_question7
) x
fiddle demo
Try this
DECLARE #q_id NVARCHAR(50)
begin
SET #q_id='JEE_PHY_1'
UPDATE oems_question7 SET q_id = SUBSTRING (#q_id ,0,CHARINDEX('_', #q_id,5) + 1 ) + ' ' + CONVERT(NVARCHAR,CONVERT(INT, SUBSTRING ( #q_id ,CHARINDEX('_', #q_id,5) + 1, LEN(#q_id) )) +1)
WHERE id between 1 and 40
end
I have a TSQL statement that is taking several hours to run. I'm sure I need to look into the import process to avoid duplicates being inserted but for the time being I'd just like to remove all records except one with duplicate values. ParameterValueId is the primary key on the table but I have many duplicate entries that need to be deleted. I only need one record for each ParameterId, SiteId, MeasurementDateTime, and ParameterValue. Below is my current method for deleting duplicate records. It finds all values that have a count > 1. It then finds the first Id with those values and deletes all of the records with those values that don't match the first ID found by those values. Besides the print statements is there a more efficient way of doing this. Can I do a way with the cursor at all to improve performance?
BEGIN TRANSACTION
SET NOCOUNT ON
DECLARE #BeginningRecordCount INT
SET #BeginningRecordCount =
(
SELECT COUNT(*)
FROM ParameterValues
)
DECLARE #ParameterId UNIQUEIDENTIFIER
DECLARE #SiteId UNIQUEIDENTIFIER
DECLARE #MeasurementDateTime DATETIME
DECLARE #ParameterValue FLOAT
DECLARE CDuplicateValues CURSOR FOR
SELECT
[ParameterId]
,[SiteId]
,[MeasurementDateTime]
,[ParameterValue]
FROM [ParameterValues]
GROUP BY
[ParameterId]
,[SiteId]
,[MeasurementDateTime]
,[ParameterValue]
HAVING COUNT(*) > 1
OPEN CDuplicateValues
FETCH NEXT FROM CDuplicateValues INTO
#ParameterId
,#SiteId
,#MeasurementDateTime
,#ParameterValue
DECLARE #FirstParameterValueId UNIQUEIDENTIFIER
DECLARE #DuplicateRecordsDeleting INT
WHILE ##FETCH_STATUS <> -1
BEGIN
SET #FirstParameterValueId =
(
SELECT TOP 1 ParameterValueId
FROM ParameterValues
WHERE
ParameterId = #ParameterId
AND SiteId = #SiteId
AND MeasurementDateTime = #MeasurementDateTime
AND ParameterValue = #ParameterValue
)
SET #DuplicateRecordsDeleting =
(
SELECT COUNT(*)
FROM ParameterValues
WHERE
ParameterId = #ParameterId
AND SiteId = #SiteId
AND MeasurementDateTime = #MeasurementDateTime
AND ParameterValue = #ParameterValue
AND ParameterValueId <> #FirstParameterValueId
)
PRINT 'DELETING ' + CAST(#DuplicateRecordsDeleting AS NVARCHAR(50))
+ ' records with values ParameterId : ' + CAST(#ParameterId AS NVARCHAR(50))
+ ', SiteId : ' + CAST (#SiteId AS NVARCHAR(50))
+ ', MeasurementDateTime : ' + CAST(#MeasurementDateTime AS NVARCHAR(50))
+ ', ParameterValue : ' + CAST(#ParameterValue AS NVARCHAR(50))
DELETE FROM ParameterValues
WHERE
ParameterId = #ParameterId
AND SiteId = #SiteId
AND MeasurementDateTime = #MeasurementDateTime
AND ParameterValue = #ParameterValue
AND ParameterValueId <> #FirstParameterValueId
FETCH NEXT FROM CDuplicateValues INTO
#ParameterId
,#SiteId
,#MeasurementDateTime
,#ParameterValue
END
CLOSE CDuplicateValues
DEALLOCATE CDuplicateValues
DECLARE #EndingRecordCount INT
SET #EndingRecordCount =
(
SELECT COUNT(*)
FROM ParameterValues
)
PRINT 'Beginning Record Count : ' + CAST(#BeginningRecordCount AS NVARCHAR(50))
PRINT 'Ending Record Count : ' + CAST(#EndingRecordCount AS NVARCHAR(50))
PRINT 'Total Records Deleted : ' + CAST((#BeginningRecordCount - #EndingRecordCount) AS NVARCHAR(50))
SET NOCOUNT OFF
PRINT 'RUN THE COMMIT OR ROLLBACK STATEMENT AFTER VERIFYING DATA.'
--COMMIT
--ROLLBACK
Use option with CTE and OVER clause. OUTPUT.. INTO clause saves the information from rows affected by an DELETE statement into #delParameterValues table. Further, in the body of procedure, you can use this table to print the affected rows.
DECLARE #delParameterValues TABLE
(
ParameterId UNIQUEIDENTIFIER,
SiteId UNIQUEIDENTIFIER,
MeasurementDateTime DATETIME,
ParameterValue FLOAT,
DeletedRecordCount int
)
;WITH cte AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY [ParameterId],[SiteId],[MeasurementDateTime],[ParameterValue] ORDER BY 1/0) AS rn,
COUNT(*) OVER (PARTITION BY [ParameterId],[SiteId],[MeasurementDateTime],[ParameterValue]) AS cnt
FROM [ParameterValues]
)
DELETE cte
OUTPUT DELETED.[ParameterId],
DELETED.[SiteId],
DELETED.[MeasurementDateTime],
DELETED.[ParameterValue],
DELETED.cnt INTO #delParameterValues
WHERE rn != 1
SELECT DISTINCT *
FROM #delParameterValues
Demo on SQLFiddle
you can do it in a single sql:
DELETE p FROM ParameterValues p
LEFT JOIN
(SELECT ParameterId, SiteId, MeasurementDateTime, ParameterValue, MAX(ParameterValueId) AS MAX_PARAM
FROM ParameterValues
GROUP BY ParameterId, SiteId, MeasurementDateTime, ParameterValue
) m
ON m.ParameterId = p.ParameterId
AND m.SiteId = p.SiteId
AND m.MeasurementDateTime = p.MeasurementDateTime
AND m.ParameterValue = p.ParameterValue
AND m.MAX_PARAM = p.ParameterValueId
WHERE m.ParameterId IS NULL
Of course it will not print the output, but you can still print the rows before and after