I need to find customers who have made identical orders. (Using T-SQL)
Order
OrderID Customerer
1 2
2 5
3 6
4 2
5 4
6 6
7 8
OrderLine
OrderLineID OrderID OrderDate OrderType Quantity Reference
1 1 01/01/2011 1 1 Coca Cola
2 1 01/01/2011 1 3 Tea
3 2 02/02/2011 2 1 Coffee
4 2 02/02/2011 2 2 Solo
5 2 03/02/2011 1 1 Soda
6 3 03/02/2011 1 3 Tea
7 3 03/02/2011 1 1 Coca Cola
8 4 05/06/2011 1 1 Beer
9 5 06/06/2011 2 1 Tea
10 5 06/06/2011 2 1 Coca Cola
11 6 07/07/2011 1 1 Coffee
12 6 07/07/2011 1 2 Solo
13 6 07/07/2011 1 1 Soda
14 6 07/07/2011 1 1 Beer
15 7 08/08/2011 1 1 Beer
Here orders with OrderID 1 and 3 are considered to be identical because the number for orderlines, "Quantity" and "Reference" are identical on both orders. Meaning that customer 2 and 6 have placed identical orders.
Order 5 are not identical to order 1 and 3 because Quantity differ.
Order 2 are not identical to order 6 because orderlines differ.
Order 4 and 7 are also identical.
I am searching for a ressult like this:
IdenticalOrders
OrderID CustomeerID
1 2
3 6
4 2
7 8
It seems like an easy task, but I just can't understand where to start.
(I am still new to t-sql :-) )
Here's one way.
SELECT O1.OrderID ,
O1.Customer ,
O2.OrderID ,
O2.Customer
FROM [Order] O1
JOIN [Order] O2 ON O1.OrderID < O2.OrderID
AND O1.Customer <> O2.Customer
WHERE NOT EXISTS ( SELECT Quantity ,
Reference
FROM OrderLine
WHERE O1.OrderID = OrderLine.OrderID
EXCEPT
SELECT Quantity ,
Reference
FROM OrderLine
WHERE O2.OrderID = OrderLine.OrderID )
AND NOT EXISTS ( SELECT Quantity ,
Reference
FROM OrderLine
WHERE O2.OrderID = OrderLine.OrderID
EXCEPT
SELECT Quantity ,
Reference
FROM OrderLine
WHERE O1.OrderID = OrderLine.OrderID )
You can also use XML PATH to simulate GROUP_CONCAT then JOIN the two result sets
DECLARE #T TABLE
(
OrderId INT PRIMARY KEY,
Customer INT ,
complete_order VARCHAR(MAX)
)
INSERT INTO #T
SELECT *
FROM [Order] O
CROSS APPLY ( SELECT CAST(Quantity AS VARCHAR(30))
+ '~' + Reference + '~~'
FROM OrderLine OL
WHERE OL.OrderID = O.OrderID
ORDER BY Reference ,
Quantity
FOR
XML PATH('')
) T ( complete_order )
SELECT T1.OrderId,
T1.Customer
FROM #T T1
WHERE EXISTS ( SELECT *
FROM #T T2
WHERE T1.Customer <> T2.Customer
AND T1.OrderId <> T2.OrderId
AND T1.complete_order = T2.complete_order )
This is an extension of Martin's second suggestion. This will show all matching combinations without any repetitions.
;With FmtOL(customer, orderid, complete_order) as
(
SELECT customer, orderid, complete_order
FROM Order O
cross apply ( SELECT CAST(Quantity AS VARCHAR(30))
+ '~' + Reference + '~~'
FROM OrderLine OL
WHERE OL.OrderID = O.OrderID
ORDER BY Reference ,
Quantity
FOR
XML PATH('')
) T ( complete_order )
)
SELECT T1.OrderId,
T1.Customer,
STUFF(C1.a, 1, 2, '') as [SameAs]
FROM FmtOL T1
Cross apply ( SELECT '; ' + 'Customer ' + Cast(T2.Customer as varchar(30))
+ '''s order ' + Cast(T2.OrderID as varchar(30))
FROM FmtOL T2
WHERE T1.Customer < T2.Customer
AND T1.OrderId < T2.OrderId
AND T1.complete_order = T2.complete_order
order by ';' + Cast(T2.Customer as varchar(30))
+ '''s order ' + Cast(T2.OrderID as varchar(30))
, t2.orderid
for xml path('')
) C1 (a)
where C1.a is not null
Results should look like this:
OrderId Customer SameAs
1 2 Customer 6's order 3
4 2 Customer 8's order 7
Here's the most simple approach.
-- sample table
create table x
(
LineId int identity(1, 1)
,InvoiceFk int
,ProductFk int
,Quantity int
)
-- sample data
insert into x
(InvoiceFk, ProductFk, Quantity) values
(11, 1, 1)
,(11, 2, 1)
,(11, 3, 1)
,(12, 1, 2)
,(12, 2, 2)
,(12, 3, 2)
,(13, 1, 3)
,(13, 2, 3)
,(13, 3, 3)
-- your order, probably from a parameter
declare #order table
(
InvoiceFk int
,ProductFk int
,Quantity int
)
insert into #order
(InvoiceFk, ProductFk, Quantity) values
(14, 1, 1) -- duplicate invoice 11
,(14, 2, 1)
,(14, 3, 1)
-- your order unique checksum
declare #orderCheck int
select #orderCheck = checksum_agg(checksum(ProductFk, Quantity))
from #order
-- test your order in existing data
declare #match int
select #match =
(
select TOP 1 InvoiceFk from
(
select
InvoiceFk
,checksum_agg(Col1) as Col2
from
(
select
InvoiceFk
,checksum(productfk, quantity) as Col1
from x
) as T1
group by
InvoiceFk
) as T2
where
T2.Col2 = #orderCheck
)
-- evaluate if your order is unique or not
if (#match is not null)
begin
print 'Identical to invoice: ' + Str(#match);
end
else
begin
print 'Order is unique';
end
-- clean up sample table
drop table x
Best of luck!
Related
Brothers can you help me? Thanks
Table A
Id Name IdParent
1 Operation Null
2 Developer 1
3 Android 2
4 IOS 2
Expectes result:
ID Name
1 +Operation
2 +------ Developer
3 +------------Android
4 +------------ IOS
By adding a sequence during the recursive build, you can easily create the proper presentation sequence and nesting
Declare #YourTable table (id int,IdParent int,Name varchar(50))
Insert into #YourTable values
( 1, NULL,'Operation')
,( 2, 1 ,'Developer')
,( 3, 2 ,'Android')
,( 4, 2 ,'IOS')
,( 5, 1 ,'Poet')
,( 6, 5 ,'Limerick')
,( 7, 5 ,'Haiku')
Declare #Top int = null --<< Sets top of Hier Try 2
Declare #Nest varchar(25) = '|-----' --<< Optional: Added for readability
;with cteP as (
Select Seq = cast(10000+Row_Number() over (Order by Name) as varchar(500))
,ID
,IdParent
,Lvl=1
,Name
From #YourTable
Where IsNull(#Top,-1) = case when #Top is null then isnull(IdParent ,-1) else ID end
Union All
Select Seq = cast(concat(p.Seq,'.',10000+Row_Number() over (Order by r.Name)) as varchar(500))
,r.ID
,r.IdParent
,p.Lvl+1
,r.Name
From #YourTable r
Join cteP p on r.IdParent = p.ID)
Select A.ID
,A.IdParent
,A.Lvl
,Name = Replicate(#Nest,A.Lvl-1) + A.Name
From ctep A
Order By A.Seq
Returns
ID IdParent Lvl Name
1 NULL 1 Operation
2 1 2 |-----Developer
3 2 3 |-----|-----Android
4 2 3 |-----|-----IOS
5 1 2 |-----Poet
7 5 3 |-----|-----Haiku
6 5 3 |-----|-----Limerick
Here's another version:
WITH RawData AS (
SELECT 1 AS Id, 'Operation' AS Name, CONVERT(INT, NULL) AS IdParent
UNION ALL
SELECT 2 AS Id, 'Developer' AS Name, 1 AS IdParent
UNION ALL
SELECT 3 AS Id, 'Android' AS Name, 2 AS IdParent
UNION ALL
SELECT 4 AS Id, 'IOS' AS Name, 2 AS IdParent),
Depth AS (
SELECT
Id,
1 AS depth,
IdParent
FROM
RawData
UNION ALL
SELECT
d.Id,
d.depth + 1,
r.IdParent
FROM
Depth d
INNER JOIN RawData r ON r.Id = d.IdParent),
MaxDepth AS (
SELECT
Id,
MAX(depth) AS depth
FROM
Depth
GROUP BY
Id)
SELECT
r.Id,
'+' + REPLICATE('----', m.depth - 1) + r.Name AS Name
FROM
RawData r
INNER JOIN MaxDepth m ON m.Id = r.Id;
Results:
Id Name
1 +Operation
2 +----Developer
3 +--------Android
4 +--------IOS
DECLARE #mockup TABLE(Id INT, Name VARCHAR(100), IdParent INT);
INSERT INTO #mockup VALUES
(1,'Operation',Null)
,(2,'Developer',1)
,(3,'Android',2)
,(4,'IOS',2);
--The query uses a recursive CTE and finally REPLICATE with the recursive level to add the number of hyphens...
WITH recCTE AS
(
SELECT Id, Name, 1 AS Lvl, CAST(REPLACE(STR(ROW_NUMBER() OVER (ORDER BY Id),5),' ','0') AS VARCHAR(MAX)) AS Seq
FROM #mockup
WHERE IdParent IS NULL
UNION ALL
SELECT m.Id,m.Name,r.Lvl +1,r.Seq + '.' + REPLACE(STR(ROW_NUMBER() OVER (ORDER BY m.Id),5),' ','0')
FROM #mockup AS m
INNER JOIN recCTE AS r ON m.IdParent=r.Id
)
SELECT *
,'+' + REPLICATE('-',Lvl*4) + Name
FROM recCTE
ORDER BY Seq
the result
+----+-----------+-----+----------------------+
| Id | Name | Lvl | (Kein Spaltenname) |
+----+-----------+-----+----------------------+
| 1 | Operation | 1 | +----Operation |
+----+-----------+-----+----------------------+
| 2 | Developer | 2 | +--------Developer |
+----+-----------+-----+----------------------+
| 3 | Android | 3 | +------------Android |
+----+-----------+-----+----------------------+
| 4 | IOS | 3 | +------------IOS |
+----+-----------+-----+----------------------+
I have the following tables 7 tables:
1) Titles
ID Title Author
-------------------------------------------------------------------------
1 The Hidden Language of Computer Hardware and Software Charles Petzold
2 Paths, Dangers, Strategies Nick Bostrom
3 The Smart Girl's Guide to Privacy Violet Blue
4 Introduction to Algorithms Thomas H. Cormen
5 Machine Learning in Action Peter Harrington
...
2) Themes
ID Name
------------------------------------------
1 Science Fiction
2 Biography
3 Painting
...
3) Subjects
ID Name
-----------------------------------
1 Science
2 Technology
3 Music
4 Geography
...
4) Grades
ID Name
------------------------------------
1 Grade 1
2 Grade 2
3 Grade 3
4 Grade 4
5 Grade 5
...
5) TitleThemeAssociation
TitleID ThemeID
------------------------------------------
1 1
1 3
4 2
4 3
...
6) TitleSubjectAssociaton
TitleID SubjectID
---------------------------------
1 1
1 3
2 1
2 3
4 1
4 2
...
7) TitleGradeAssociaton
TitleID GradeID
1 1
1 2
1 3
2 1
2 2
...
I have a stored procudure as below:
CREATE PROCEDURE [dbo].[GetTitlesPageWise]
#PageIndex INT = 1
,#PageSize INT = 10
,#searchText NVARCHAR(250) = ''
,#PageCount INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SET FMTONLY OFF;
select ROW_NUMBER() over
(
ORDER BY [id] ASC
) as RowNumber,
T.Id As [Title ID],
T.Title,
H.Theme,
S.Subject,
G.Grade
into #Results
From Titles T
Outer Apply
(
Select Stuff(( Select ', ' + Name
From Themes H
Join TitleThemeAssociaton TH On H.Id = TH.ThemeId
Where TH.TitleId = T.Id
For Xml Path('')), 1, 2, '') As Theme
From Themes
) H
Outer Apply
(
Select Stuff(( Select ', ' + Name
From Subjects S
Join TitleSubjectAssociation TS On S.Id = TS.SubjectId
Where TS.TitleId = T.Id
For Xml Path('')), 1, 2, '') As Subject
From Subjects
) S
Outer Apply
(
Select Stuff(( Select ', ' + Name
From Grades G
Join TitleGradeAssociation TG On G.Id = TG.GradeId
Where TG.TitleId = T.Id
For Xml Path('')), 1, 2, '') As Grade
From Grades
) G
WHERE
t.title Like #searchText + '%'
AND
(
H.Theme Is Null
Or S.Subject Is Null
Or G.Grade Is Null
)
DECLARE #RecordCount INT
SELECT #RecordCount = COUNT(*) FROM #Results
SET #PageCount = CEILING(CAST(#RecordCount AS DECIMAL(10, 2)) / CAST(#PageSize AS DECIMAL(10, 2)))
PRINT #PageCount
SELECT * FROM #Results
WHERE RowNumber BETWEEN(#PageIndex -1) * #PageSize + 1 AND(((#PageIndex -1) * #PageSize + 1) + #PageSize) - 1
DROP TABLE #Results
END
I want distinct title ID and output is like as below but the query is giving the duplicate result. If Theme, Subject and Grade all there values are assigned that record should be excluded form the result. In the above case Title ID 1 should be excluded because all three values are present there. I need help to fix the problem.
RowNumber Title ID Title Theme Subject Grade
1 2 Paths, Dangers, Strategies NULL Science , Music Grade 1, Grade 2
2 3 The Smart Girl's Guide to Privacy NULL NULL NULL
3 4 Introduction to Algorithms Biography, Painting Science , Technology NULL
4 5 Machine Learning in Action NULL NULL NULL
.............
SQL DEMO
SELECT
ROW_NUMBER() OVER (ORDER BY [ID]) rn,
[ID],
[Title],
Subject = STUFF ((
SELECT ',' + S.[Name]
FROM TitleSubjectAssociation TS
JOIN Subjects S
ON TS.SubjectId = S.Id
WHERE TS.[TitleID] = T.[ID]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''
),
Theme = STUFF ((
SELECT ',' + TH.[Name]
FROM TitleThemeAssociation TA
JOIN Themes TH
ON TA.[ThemeID] = TH.[ID]
WHERE TA.[TitleID] = T.[ID]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''
),
Grade = STUFF ((
SELECT ',' + G.[Name]
FROM TitleGradeAssociation TG
JOIN Grades G
ON TG.[GradeID] = G.[ID]
WHERE TG.[TitleID] = T.[ID]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''
)
FROM Titles T;
If you need a filter you can try checking how many , are in the results. two , mean three elements.
WHERE len(Subject) - len(replace(Subject,',','')) != 2
AND len(Theme) - len(replace(Theme,',','')) != 2
AND len(Grade) - len(replace(Grade,',','')) != 2
OUTPUT
I have a scenario in which there are two tables , one is MAIN and second is Child.
There are possible 4 status Types
1 = Not Started
2 = Started
3 = Running
4 = Stopped
I have random records in child for each main tables's row , I need to find out only the records count in which Child's table Only Status (1,4) used.
below I am attaching the script.
CREATE TABLE #Main
(
ID INT ,
CreateDateTime DATETIME ,
LLevel INT
)
CREATE TABLE #Child
(
ID INT ,
MainID INT ,
STATUS INT
)
-- Status(1= NotStarted, 2= Started ,3 = Ruunning ,4 = Stopped)
INSERT INTO #Main
SELECT 1 ,
'2015-12-24 18:48:41' ,
1
UNION ALL
SELECT 2 ,
'2015-12-24 18:49:59' ,
3
UNION ALL
SELECT 3 ,
'2015-12-24 18:51:01' ,
1
UNION ALL
SELECT 4 ,
'2015-12-24 18:53:11' ,
4
UNION ALL
SELECT 5 ,
'2015-12-24 18:57:11' ,
2
INSERT INTO #Child
SELECT 1 ,
1 ,
1 -- MIAN ID = 1
UNION ALL
SELECT 2 ,
1 ,
2
UNION ALL
SELECT 3 ,
1 ,
3
UNION ALL
SELECT 4 ,
2 ,
1 -- MIAN ID = 2
UNION ALL
SELECT 5 ,
2 ,
4
UNION ALL
SELECT 6 ,
3 ,
1 -- MIAN ID = 3
UNION ALL
SELECT 7 ,
3 ,
2
UNION ALL
SELECT 8 ,
3 ,
3
UNION ALL
SELECT 9 ,
4 ,
1 -- MIAN ID = 4
UNION ALL
SELECT 10 ,
4 ,
2
UNION ALL
SELECT 11 ,
4 ,
3
UNION ALL
SELECT 12 ,
5 ,
1 -- MIAN ID = 2
UNION ALL
SELECT 13 ,
5 ,
4
SELECT *
FROM #Main
SELECT *
FROM #Child
ORDER BY MainID ASC
DROP TABLE #Main
DROP TABLE #Child
I am attaching an image, these records i need , means count should be two.
This may not be the fastest way to do it, but the optimizer will make it close. Also, it is clear to see that is is correct and understand how it works using CTEs
WITH haveone as
(
SELECT DISTINCT MainID as ID
FROM #Child
WHERE STATUS = 1
), havefour as
(
SELECT DISTINCT MainID as ID
FROM #Child
WHERE STATUS = 4
), haveboth as
(
SELECT haveone.ID
FROM haveone
JOIN havefour ON haveone.ID = havefour.ID
), haveother as
(
SELECT DISTINCT MainID as ID
FROM #Child
WHERE STATUS NOT IN (1,4)
)
SELECT ID
FROM haveboth
WHERE ID NOT IN (SELECT ID FROM haveother)
I have figure out and produced result this way.
SELECT count(m.id) FROM #main m INNER JOIN #child c on m.id = c.mainid and c.status in (1,4)
and c.mainid not in (select mainid from #child where status in (2,3)) group by m.id
having count(m.id) = 2
I have a table which has employee details
EmpId ManagerId Level Value
1 0 5 CEO
2 1 4 EMP
3 1 4 ORG
4 2 3 NULL
5 2 3 NULL
6 2 2 NULL
7 1 1 NULL
8 5 0 NULL
Now, I have to start wil Employee Id 2 and found all it's low level hirerachy (i.e. 2, 4, 5, 6, 8) and assign them value same as "2" (i.e. EMP).
Expected output :
EmpId ManagerId Level Value
1 0 5 CEO
2 1 4 EMP
3 1 4 ORG
4 2 3 EMP
5 2 3 EMP
6 2 2 EMP
7 1 1 NULL
8 5 0 EMP
What I am trying:
; WITH LevelHire AS
(
SELECT EmpId, ManagerId,Level
FROM EmployeeTable
WHERE EmpId =2
UNION ALL
SELECT Lh.EmpId, RC.ManagerId, Lh.Level
FROM LevelHire LH
INNER JOIN [EmployeeTable] RC
ON LH.EmpId= RC.EmpId
)
SELECT * FROM LevelHire
option (maxrecursion 0)
How can I achieve the same?
you can try something like this
;WITH EmployeeTable AS
(
SELECT 1 EmpId,0 ManagerId , 5 Level ,'CEO' Value
UNION ALL SELECT 2,1, 4,'EMP'
UNION ALL SELECT 3,1, 4,'ORG'
UNION ALL SELECT 4,2, 3,NULL
UNION ALL SELECT 5,2, 3,NULL
UNION ALL SELECT 6,2, 2,NULL
UNION ALL SELECT 7,1, 1,NULL
UNION ALL SELECT 8,5, 0,NULL
),LevelHire AS
(
SELECT EmpId, ManagerId,Level,Value
FROM EmployeeTable
WHERE EmpId = 2
UNION ALL
SELECT RC.EmpId, RC.ManagerId, Lh.Level,LH.Value
FROM LevelHire LH
INNER JOIN [EmployeeTable] RC
ON LH.EmpId= RC.ManagerId
)
SELECT E.EmpId, E.ManagerId,E.Level,ISNULL(E.Value ,LH.Value) Value
FROM EmployeeTable E
LEFT JOIN LevelHire LH
ON E.EmpId = LH.EmpId
Time to learn about hierarchyid. First, some code:
IF object_id('tempdb.dbo.#employees') IS NOT NULL
DROP TABLE #employees;
go
WITH Employees AS (
SELECT *
FROM ( VALUES
( 1, NULL, 5, 'CEO'),
( 2, 1, 4, 'EMP'),
( 3, 1, 4, 'ORG'),
( 4, 2, 3, NULL ),
( 5, 2, 3, NULL ),
( 6, 2, 2, NULL ),
( 7, 1, 1, NULL ),
( 8, 5, 0, NULL )
) AS x ( EmpId, ManagerId, Level, Value )
), rcte AS (
SELECT e.EmpId ,
e.ManagerId ,
e.Level ,
e.Value,
CAST('/' + CAST(e.EmpId AS VARCHAR) + '/' AS VARCHAR(MAX)) AS h
FROM Employees AS e
WHERE e.ManagerId IS NULL
UNION ALL
SELECT e.EmpId ,
e.ManagerId ,
e.Level ,
e.Value ,
m.h + CAST(e.EmpId AS VARCHAR) + '/' AS h
FROM Employees AS e
JOIN rcte AS m
ON e.ManagerId = m.EmpId
)
SELECT rcte.EmpId ,
rcte.ManagerId ,
rcte.Level ,
rcte.Value ,
CAST(rcte.h AS HIERARCHYID) AS h
INTO #employees
FROM rcte;
GO
SELECT e.EmpId ,
e.ManagerId ,
e.Level ,
e.Value ,
e.h.ToString() AS h
FROM #employees AS e
JOIN #employees AS m
ON e.h.IsDescendantOf(m.h) = 1
WHERE m.EmpId = 1
SELECT m.EmpId ,
m.ManagerId ,
m.Level ,
m.Value ,
m.h.ToString() AS h
FROM #employees AS e
JOIN #employees AS m
ON e.h.IsDescendantOf(m.h) = 1
WHERE e.EmpId = 8
While I needed a recursive CTE to actually establish the hierarchy, any of the actual queries of the form "who does this person report to?" and "who reports to this person?" are ultimately satisfied from the persisted hierarchy in the #employees table. The two queries at the end show how to traverse the hierarchy in either direction. This sort of thing is important if your hierarchy is large (wide, deep, or both). You do need to maintain it when the org chart changes, but that's a one-time operation. The querying of the data should be fast because the lineage is persisted with the employee record.
Incidentally, your Level column is a bit odd to me. Specifically, it seems backwards (i.e. CEO has the highest level). I say this because if/when you add another level to the org chart, you'll need to re-level everyone from the CEO down. If you have the CEO have the lowest level, you just tack that level onto the bottom and don't have to re-level anyone.
I need help building a query, which returns the Minimum difference between Value + another Value from same table and the other ID that gave the result (plus the sum can't be sum of the value itself)
Table:
ID Value
1 1
2 2
3 5
4 -10
5 -5
6 3
7 -15
Expected result:
ID Value MinDif IDofTheOtherValue
1 1 3 2 <-- MinDif = 1 + 2 (ID 1 + ID 2)
2 2 3 1 <-- MinDif = 2 + 1 (ID 2 + ID 1)
3 5 0 5 <-- MinDif = 5 + -5 (ID 3 + ID 5)
4 -10 -5 3 <-- MinDif = -10 + 5 (ID 4 + ID 3)
5 -5 0 3 <-- MinDif = -5 + 5 (ID 5 + ID 3)
6 3 -2 5 <-- MinDif = 3 + -5 (ID 6 + ID 5)
7 -15 -10 3 <-- MinDif = -15 + 5 (ID 7 + ID 3)
Here's a query to create the table:
DECLARE #myTable TABLE(ID int, Value int)
INSERT INTO #myTable VALUES (1, 1), (2,2), (3, 5), (4, -10), (5, -5), (6, 3), (7, -15)
And here's what I have tried, but this gives an SQL error (Cannot perform an aggregate function on an expression containing an aggregate or a subquery.)
SELECT m.ID, MIN(ABS(m.Value + (SELECT m2.Value FROM #myTable m2)))
FROM #myTable m
This is giving your required results:
with diffRank as
(
select ID = t1.ID
, minDif = t1.value + t2.value
, IDofTheOtherValue = t2.ID
, diffRank = row_number() over (partition by t1.ID order by abs(t1.value + t2.value), t2.ID)
from #myTable t1
inner join #myTable t2 on t1.ID <> t2.ID
)
select ID
, minDif
, IDofTheOtherValue
from diffRank
where diffRank = 1
order by ID;
SQL Fiddle with demo.
I resolved this by myself. Here's the Select clause:
SELECT tab.ID, tab.Value, test.*
FROM #myTable tab
OUTER APPLY
(SELECT TOP 1 ID AS [AnotherID], [SUM]
FROM
(
SELECT m.ID, m2.ID AS [ID2], m.Value + m2.Value AS [SUM]
FROM #myTable m
JOIN #myTable m2 ON m2.ID <> m.ID
) apu WHERE ID2 = tab.ID ORDER BY ABS([SUM])) test
In Oracle I would do:
select x.id, (select min(abs(x.value + y.value)) from my_table y),
(select first value (y.id) over (order by abs(x.value + y.value))
from my_table y)
from my_table x
think of something similar in TSQL
Try this.. it should work.
DECLARE #myTable TABLE(ID int, Value int)
INSERT INTO #myTable VALUES (1, 1), (2,2), (3, 5), (4, -10), (5, -5), (6, 3), (7, -15)
SELECT C.ID, C.Value
, C.Value + (SELECT TOP 1 E.Value FROM #myTable E WHERE C.AbsMinDif = ABS(C.Value + E.Value) ORDER BY E.ID) MinDif
, (SELECT TOP 1 F.ID FROM #myTable F WHERE C.AbsMinDif = ABS(C.Value + F.Value) ORDER BY F.ID) IDofTheOtherValue
FROM (
SELECT A.ID, MIN(A.Value) Value, MIN(ABS(A.Value + B.Value)) AbsMinDif
FROM #myTable A
CROSS JOIN #myTable B
WHERE A.ID <> B.ID
GROUP BY A.ID
) C