Select Minimum difference of summary of two columns in T-SQL - sql-server

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

Related

Add incrementally row values of a column of type string

I have a table with the following values
UserID ParentID Levels Path
1 NULL 0 A1
5 1 1 A2
9 5 2 A3
43 9 3 A4
The output should be like followed :
UserID ParentID Levels FinalPath
1 NULL 0 A1/
5 1 1 A1/A2/
9 5 2 A1/A2/A3/
43 9 3 A1/A2/A3/A4/
Thanks in advance for any guidance on this.
Solution using a recusive common table expression.
Sample data
create table users
(
userid int,
parentid int,
levels int,
path nvarchar(100)
);
insert into users (userid, parentid, levels, path) values
(1, NULL, 0, 'A1'),
(5, 1, 1, 'A2'),
(9, 5, 2, 'A3'),
(43, 9, 3, 'A4');
Solution
with cte as
(
select u.userid, u.parentid, u.levels, u.path
from users u
where u.parentid is null
union all
select u.userid, u.parentid, u.levels, convert(nvarchar(100), c.path + '/' + u.path)
from users u
join cte c
on c.userid = u.parentid
)
select c.userid, c.parentid, c.levels, c.path + '/' as FinalPath
from cte c;
Fiddle
Here's a version that both calculates the Level and appends the Path.
Data
drop table if exists dbo.test_table;
go
create table dbo.test_table(
UserID int,
ParentID int,
[Path] varchar(5));
insert dbo.test_table([UserID], [ParentID], [Path]) values
(1,null, 'A1'),
(5,1, 'A2'),
(9,5, 'A3'),
(43,9, 'A4');
Query
;with recur_cte([UserId], [ParentID], h_level, [Path]) as (
select [UserId], [ParentID], 0, cast([Path] as varchar(100))
from dbo.test_table
where [ParentID] is null
union all
select tt.[UserId], tt.[ParentID], rc.h_level+1, cast(concat(tt.[Path], '/', rc.[Path]) as varchar(100))
from dbo.test_table tt join recur_cte rc on tt.[ParentID]=rc.[UserId])
select * from recur_cte;
Results
UserId ParentID h_level Path
1 NULL 0 A1
5 1 1 A1/A2
9 5 2 A1/A2/A3
43 9 3 A1/A2/A3/A4

Aggregate count of different columns

There are two table:
Table a:
id name b_id1 b_id2 b_id3 b_id4
1 a 10 8 null null
2 b 3 4 8 10
3 c 10 5 4 null
Table B
b_id title
3 Value3
4 Value4
5 Value 5
8 Value 8
10 Value
Table A has F.K on b_id1,b_id2,b_id3,b_id4 to table B on b_id cloumn,
We are going to group on Title and count it.
something like following result:
Title Count
Value3 1
Value4 2
Value5 1
Value8 2
Value10 3
Null 3
Full working example:
DECLARE #Table1 TABLE
(
[id] INT
,[name] VARCHAR(1)
,[b_id1] INT
,[b_id2] INT
,[b_id3] INT
,[b_id4] INT
);
DECLARE #Table2 TABLE
(
[b_id] INT
,[title] VARCHAR(7)
);
INSERT INTO #Table1 ([id], [name], [b_id1], [b_id2], [b_id3], [b_id4])
VALUES (1, 'a', 10, 8, NULL, NULL)
,(2, 'b', 3, 4, 8, 10)
,(3, 'c', 10, 5, 4, NULL);
INSERT INTO #Table2 ([b_id], [title])
VALUES (3, 'Value3')
,(4, 'Value4')
,(5, 'Value 5')
,(8, 'Value 8')
,(10, 'Value');
SELECT T2.[title]
,COUNT(*)
FROM
(
SELECT [b_id1]
FROM #Table1
UNION ALL
SELECT [b_id2]
FROM #Table1
UNION ALL
SELECT [b_id3]
FROM #Table1
UNION ALL
SELECT [b_id4]
FROM #Table1
) DS
LEFT JOIN #Table2 T2
ON DS.[b_id1] = T2.[b_id]
GROUP BY T2.[title];
One way would be like below:Demo Here
;with cte
as
(select title,count(b_id) as val from table1 t
join
table2 t2
on t.id=t2.b_id
or
t.b_id1=t2.b_id
or
t.b_id2=t2.b_id
or
t.b_id3=t2.b_id
or
t.b_id4=t2.b_id
group by title
)
select * from cte
union all
select null,sum(case
when b_id1 is null then 1 else 0 end)+
sum(case
when b_id2 is null then 1 else 0 end)
+sum(case
when b_id3 is null then 1 else 0 end)
+sum(case
when b_id4 is null then 1 else 0 end)
from table1
output:
Value 3
Value 5 1
Value 8 2
Value3 2
Value4 2
NULL 3

How to find the parent ID in a return table

I have following table:
ID ParentID
1 NULL
2 1
3 2
4 NULL
5 4
6 5
7 3
I want to find the first ID of a specific child ID.
Example: ID=7 and the result is 1
ID=6 and the result is 4
How to do it?
You need to do a bit of recursive CTE magic to solve this one.
Given the data in a table variable thusly:
declare #data table(id int, parentid int)
insert into #data
select 1, null
union select 2,1
union select 3,2
union select 4, null
union select 5,4
union select 6,5
union select 7,3
The following should do the trick:
;with recursiveTable as
(
select d.id, d.parentId, 0 as depth
from #data d
where d.id=6 -- Parameterize this
union all
select d.id, d.parentid, r.depth-1
from #data d
inner join recursiveTable r
on d.id = r.parentId
)
select top 1 id
from recursiveTable
order by depth
Plugging 6 in as above returns 4. Changing this to 7 returns 1 as requested.
Try this:
CREATE TABLE childpar(ID int,ParentID int)
INSERT INTO childpar
values(1,NULL),
(2, 1),
(3, 2),
(4, NULL),
(5, 4),
(6, 5),
(7, 3)
DECLARE #inpyID int
SET #inpyID=7
;WITH CTE1 as (
select * from childpar where id=#inpyID
union all
select c2.* from CTE1 c1 inner join childpar c2 on c1.ParentID = c2.ID
)
select top 1 id from CTE1 order by id asc

Finding customers with identical orders

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!

T-SQL data transformation

I have a result set like this:
ID Value
1 100
2 50
3 200
4 30
- -
- -
I want it to transform into following:
Value1 Value2
100 50
200 30
- -
- -
How to do it with T-SQL?
Use this:
select a.Value, b.Value
from
(
select row_number() over(order by ID) [rn], Value
from #t
)a
left join
(
select row_number() over(order by ID) [rn], Value
from #t
)b on b.rn = a.rn + 1
where a.rn % 2 = 1
Sample data:
declare #t table (ID int, Value int)
insert #t values (1,100), (2,50), (3,200), (4,30)
Output:
Value Value
----------- -----------
100 50
200 30
declare #t table (id int, v int)
insert #t values (1, 10)
insert #t values (2, 20)
insert #t values (3, 30)
insert #t values (4, 40)
insert #t values (5, 50)
select t1.v, t2.v
from #t t1
left join #t t2
on t1.id + 1 = t2.id
where t1.id %2 = 1

Resources