SQL Query without Temporary Table - sql

I have written a query which works great on my local SQL Server 2005. I uploaded the query to my hosting server and somehow they say that temporary table creation is disabled on their server.
My query looks like this
create table #tmp
(
srno int identity (1,1) ,
orderid int,
orderdate datetime,
product_code varchar(255),
product_name varchar(255),
shipping_cost decimal(18,2)
)
insert into #tmp (orderid, orderdate, product_code, product_name, shipping_cost)
(select distinct
ord.orderid, ord.orderdate, odn.productcode,
odn.productname, ord.totalshippingcost
from OrderNew ord
inner join order_detailsnew odn on ord.orderid = odn.orderid)
declare #rowcount int, #flag int, #orderid int
set #rowcount = (select ##ROWCOUNT)
set #flag = 0
while (#flag <#rowcount)
begin
set #orderid = (select orderid from #tmp where srno = #flag + 1)
if exists (select 1 from #tmp where orderid = #orderid )
begin
update #tmp
set shipping_cost = 0.0
where srno IN (select srno from #tmp
where orderid = #orderid
AND srno NOT IN (SELECT TOP 1 srno FROM #tmp where orderid = #orderid))
end
set #flag = #flag+1
end
select * from #tmp
drop table #tmp
So not sure if this query can be written without a temporary table, joins etc not sure if it will work ? Any advise ?

I presume this query is to feed into a report which is why you only want the total shipping cost once, while you don't need a temp table for this for reference you can always do this instead if you need to:
DECLARE #tmp TABLE
(
srno int identity (1,1) ,
orderid int,
orderdate datetime,
product_code varchar(255),
product_name varchar(255),
shipping_cost decimal(18,2)
)
and use #tmp rather than #tmp
But you shouldn't need a temp table for this, see below:
SELECT ord.orderid, ord.orderdate, odn.productcode, odn.productname, ord.totalshippingcost
FROM OrderNew AS ord
INNER JOIN order_detailsnew AS odn ON odn.orderid = ord.orderid
WHERE odn.productcode = (SELECT MIN(productcode) FROM OrderNew AS odn2 WHERE odn2.orderid = ord.orderid)
UNION ALL
SELECT ord.orderid, ord.orderdate, odn.productcode, odn.productname, 0.0 AS totalshippingcost
FROM OrderNew AS ord
INNER JOIN order_detailsnew AS odn ON odn.orderid = ord.orderid
WHERE odn.productcode > (SELECT MIN(productcode) FROM OrderNew AS odn2 WHERE odn2.orderid = ord.orderid)
ORDER BY ord.orderid, ord.orderdate, odn.productcode
Works fine for me with the following test script:
DECLARE #ord TABLE
(
orderid int,
orderdate datetime,
totalshippingcost decimal(18,2)
)
DECLARE #odn TABLE
(
orderid int,
productcode varchar(255),
productname varchar(255)
)
INSERT INTO #ord VALUES(1, CAST('20110101' AS DATETIME), 50.25)
INSERT INTO #ord VALUES(2, CAST('20110105' AS DATETIME), 78.15)
INSERT INTO #ord VALUES(3, CAST('20110112' AS DATETIME), 65.50)
INSERT INTO #ord VALUES(4, CAST('20110112' AS DATETIME), 128.00)
INSERT INTO #odn VALUES(1, 'aa', 'AAA')
INSERT INTO #odn VALUES(1, 'bb', 'BBB')
INSERT INTO #odn VALUES(1, 'cc', 'CCC')
INSERT INTO #odn VALUES(2, 'aa', 'AAA')
INSERT INTO #odn VALUES(2, 'bb', 'BBB')
INSERT INTO #odn VALUES(3, 'bb', 'BBB')
INSERT INTO #odn VALUES(3, 'cc', 'CCC')
INSERT INTO #odn VALUES(4, 'cc', 'CCC')
And my results:
Result Set (8 items)
orderid | orderdate | productcode | productname | totalshippingcost
1 | 01/01/2011 00:00:00 | aa | AAA | 50.25
1 | 01/01/2011 00:00:00 | bb | BBB | 0.00
1 | 01/01/2011 00:00:00 | cc | CCC | 0.00
2 | 05/01/2011 00:00:00 | aa | AAA | 78.15
2 | 05/01/2011 00:00:00 | bb | BBB | 0.00
3 | 12/01/2011 00:00:00 | bb | BBB | 65.50
3 | 12/01/2011 00:00:00 | cc | CCC | 0.00
4 | 12/01/2011 00:00:00 | cc | CCC | 128.00
edit: I wasn't happy with the above solution, here's a uch faster and more elegant way of doing it:
SELECT ord.orderid, ord.orderdate, ord.productcode, ord.productname, CASE WHEN row_no = 1 THEN ord.totalshippingcost ELSE 0.0 END AS totalshippingcost
FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY ord.orderid ORDER BY ord.orderid, ord.orderdate, odn.productcode) AS row_no, ord.orderid, ord.orderdate, odn.productcode, odn.productname, ord.totalshippingcost
FROM OrderNew AS ord
INNER JOIN order_detailsnew AS odn ON odn.orderid = ord.orderid
) ord
ORDER BY ord.orderid, ord.orderdate, ord.productcode
Results match perfectly.
Edit for user580950, to insert nulls into every second row:
You change the first SELECT line to be:
SELECT CASE D.N WHEN 1 THEN ord.orderid END AS orderid, ...
And you chance the ORDER BY line to be:
CROSS JOIN (SELECT 1 UNION ALL SELECT 2) AS D(N)
ORDER BY ord.orderid, ord.orderdate, ord.productcode, D.N
But as the comments say said in your other question SQL Query Add an Alternate Blank Records, this is something that you should be doing at your presentation layer and not in the database.

Related

Get exact match between two table

I have a issue that is hard to explain. I have two tables
table1: (this is something like shipping table)
ID ShippingId ProductId1 ProductId2
1 100 A A1
2 100 A A2
3 100 A A3
4 100 A A4
5 100 A A5
6 200 B B1
7 200 B B2
8 300 B A1
9 300 B A2
table2: (and this is about relation between ProductId1 and ProductId2)
ID ProductId1 ProductId2
1 A A1
2 A A2
3 A A3
4 A A4
5 A A5
6 B B1
7 B B2
In the case the shipment "100" includes all items of "A" so this should "true"
and the shipments "200" and "300" does not include all parts of their main products. So expected output should be like
ShippingId ProductId1 IsIncludeAll
100 A true
200 B false
300 A true
can you guys help me?
DECLARE #table1 AS TABLE
(
ShippingID INT,
ProductId1 INT,
ProductId2 INT
)
DECLARE #table2 AS TABLE
(
ProductId1 INT,
ProductId2 INT
)
INSERT INTO #table1 (ShippingId,ProductId1,ProductId2) VALUES (100,111, 1119)
INSERT INTO #table1 (ShippingId,ProductId1,ProductId2) VALUES (100,111, 1118)
INSERT INTO #table1 (ShippingId,ProductId1,ProductId2) VALUES (100,111, 1117)
INSERT INTO #table1 (ShippingId,ProductId1,ProductId2) VALUES (100,111, 1116)
INSERT INTO #table1 (ShippingId,ProductId1,ProductId2) VALUES (100,111, 1115)
INSERT INTO #table1 (ShippingId,ProductId1,ProductId2) VALUES (200,222, 2229)
INSERT INTO #table1 (ShippingId,ProductId1,ProductId2) VALUES (200,222, 2228)
INSERT INTO #table1 (ShippingId,ProductId1,ProductId2) VALUES (300,111, 1117)
INSERT INTO #table1 (ShippingId,ProductId1,ProductId2) VALUES (300,111, 1116)
INSERT INTO #table2 (ProductId1,ProductId2) VALUES ( 111, 1119)
INSERT INTO #table2 (ProductId1,ProductId2) VALUES ( 111, 1118)
INSERT INTO #table2 (ProductId1,ProductId2) VALUES ( 111, 1117)
INSERT INTO #table2 (ProductId1,ProductId2) VALUES ( 111, 1116)
INSERT INTO #table2 (ProductId1,ProductId2) VALUES ( 111, 1115)
INSERT INTO #table2 (ProductId1,ProductId2) VALUES ( 222, 2229)
INSERT INTO #table2 (ProductId1,ProductId2) VALUES ( 222, 2228)
ShippingId ProductId1 IsIncludeAll
100 A true
200 B false
300 A false
A total guess, as the sample DDL and DML don't match the sample data, but perhaps this?
SELECT S.ShippingID,
T2.ProductId1,
CASE COUNT(CASE WHEN T1.ProductId2 IS NULL THEN 1 END) WHEN 0 THEN 'true' ELSE 'false' END AS IsIncludeAll
FROM #table2 T2
CROSS APPLY (SELECT DISTINCT
sq.ShippingID,
sq.ProductId1
FROM #table1 sq
WHERE sq.ProductId1 = T2.ProductId1) S
LEFT JOIN #table1 T1 ON T2.ProductId1 = T2.ProductId1
AND T1.ProductId2 = T2.ProductId2
AND S.ShippingID = T1.ShippingID
GROUP BY S.ShippingID,
T2.ProductId1;
Little confused on your sample data and output. From my thought Check this query and output.
------Step 1. concatenate product1 and product2 with ShippingID wise,product1 wise order by ShippingID,ProductId1,ProductId2-------------------
declare #Shipping as table
(
ShippingID INT,
ProductId1 INT,
ProductDesc varchar(max)
)
insert #Shipping
SELECT Shipping.ShippingID,Shipping.ProductId1,
LEFT(Shipping.prod_desc,Len(Shipping.prod_desc)-1) As prod_desc
FROM
(
SELECT DISTINCT T2.ShippingID, T2.ProductId1,
(
SELECT cast(T1.ProductId1 as varchar(10))+'-' +cast(T1.ProductId2 as varchar(10))+ '|' AS [text()]
FROM dbo.table1 T1
WHERE T1.ShippingID = T2.ShippingID and T1.ProductId1=T2.ProductId1
ORDER BY T1.ShippingID,ProductId1,ProductId2
FOR XML PATH ('')
) prod_desc
FROM dbo.table1 T2
) Shipping
------Step 2. concatenate product1 and product2 with product1 wise order by ProductId1,ProductId2-------------------
declare #relation as table
(
ProductId1 INT,
ProductDesc varchar(max)
)
insert #relation
SELECT relation.ProductId1,LEFT(relation.prod_desc,Len(relation.prod_desc)-1) As prod_desc
FROM
(
SELECT DISTINCT T2.ProductId1,
(
SELECT cast(T1.ProductId1 as varchar(10))+'-' +cast(T1.ProductId2 as varchar(10))+ '|' AS [text()]
FROM dbo.table2 T1
WHERE T1.ProductId1=T2.ProductId1
ORDER BY ProductId1,ProductId2
FOR XML PATH ('')
) prod_desc
FROM dbo.table2 T2
) relation
------Step 1. use left join to match with concatinated string of every product1. if matches return True otherwise False-------------------
select a.ShippingID,a.ProductId1,case when b.ProductDesc is null then 'False' else 'True' end as IsIncludeAll
from #Shipping a
left join #relation b
on a.ProductId1=b.ProductId1 and a.ProductDesc=b.ProductDesc
-----Result-----------
------------+---------------+--------------
ShippingID | ProductId1 | IsIncludeAll
------------+---------------+--------------
100 | 111 | True
200 | 222 | True
300 | 111 | False

Avoiding GROUP BY on specific columns

I am looking at avoiding grouping by specific columns in a large query to reduce performance problems.
Example: http://sqlfiddle.com/#!18/cb98e/2
CREATE TABLE [OrderTable]
(
[id] INT,
[OrderGroupID] INT,
[Total] INT,
[fkPerson] INT,
[fkitem] INT,
PRIMARY KEY (id)
)
INSERT INTO [OrderTable] (id, OrderGroupID, Total, [fkPerson], [fkItem])
VALUES ('1', '1', '20', '1', '1'),
('2', '1', '45', '2', '2'),
('3', '2', '32', '1', '1'),
('4', '2', '30', '2', '2');
CREATE TABLE [Person]
(
[id] INT,
[Name] VARCHAR(32),
PRIMARY KEY (id)
)
INSERT INTO [Person] (id, Name)
VALUES ('1', 'Fred'),
('2', 'Sam');
CREATE TABLE [Item]
(
[id] INT,
[ItemNo] VARCHAR(32),
[Price] INT,
PRIMARY KEY (id)
)
INSERT INTO [Item] (id, ItemNo, Price)
VALUES ('1', '453', '23'),
('2', '657', '34');
Query:
WITH TABLE1 AS
(
SELECT
P.ID AS [PersonID],
P.Name,
SUM(OT.[Total]) AS [Total],
i.[id] AS [ItemID],
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rownum,
ot.fkperson
FROM
OrderTable OT
INNER JOIN
Person P ON P.ID = OT.fkperson
INNER JOIN
Item I ON I.[id] = OT.[fkItem]
GROUP BY
P.ID, P.Name, i.id, ot.fkperson
)
SELECT
*,
Totalrows = (SELECT MAX(rownum) FROM TABLE1)
FROM
TABLE1
Result:
| PersonID | Name | Total | ItemID | rownum | fkperson | Totalrows |
+----------+------+-------+--------+--------+----------+-----------+
| 1 | Fred | 52 | 1 | 1 | 1 | 2 |
| 2 | Sam | 75 | 2 | 2 | 2 | 2 |
Now, for example if i didn't want to GROUP BY a varchar column (Person Name) i could do this - remove person table and join it back later. e.g.
WITH TABLE1 AS
(
SELECT
-- P.ID AS [PersonID],
-- P.Name,
SUM(OT.[Total]) AS [Total],
i.[id] AS [ItemID],
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rownum,
ot.fkperson
FROM
OrderTable OT
-- INNER JOIN Person P ON P.ID = OT.fkperson
INNER JOIN
Item I ON I.[id] = OT.[fkItem]
GROUP BY
-- P.ID, P.Name,
i.id, ot.fkperson
)
SELECT
p.id as [PersonID],
p.Name,
t1.[total],
t1.[itemid],
t1.[rownum],
t1.fkperson
-- Totalrows = (SELECT MAX(rownum) FROM TABLE1 GROUP BY
-- i.id
-- ,ot.fkperson
-- )
FROM
TABLE1 T1
INNER JOIN
Person P ON P.ID = T1.fkperson
Result:
| PersonID | Name | total | itemid | rownum | fkperson |
+----------+------+-------+--------+--------+----------+
| 1 | Fred | 52 | 1 | 1 | 1 |
| 2 | Sam | 75 | 2 | 2 | 2 |
My issue is I also want to include the MAX(rownum) column but how can I do this in my last query without having to group by everything again? What's the best approach to this? Have I missed something really obvious? :)
You can also use CROSS APPLY. And count for the total you can use COUNT(*) OVER()
SELECT
P.id as [PersonID],
P.Name,
T1.[total],
I.id as [itemid],
ROW_NUMBER() OVER (
ORDER BY (SELECT NULL)
) AS [rownum],
T1.fkperson,
COUNT(*) OVER () Totalrows
FROM
Item I
CROSS APPLY (SELECT ot.fkperson, SUM(OT.[Total]) AS [total]
FROM OrderTable OT
WHERE I.[id] = OT.[fkItem]
GROUP BY ot.fkperson ) AS T1
INNER JOIN Person P ON P.id = t1.fkperson
Result:
PersonID Name total itemid rownum fkperson Totalrows
----------- ----------- ----------- ----------- --------- ----------- -----------
1 Fred 52 1 1 1 2
2 Sam 75 2 2 2 2

SQL: Deleting row which values already exist

I have a table that look like this:
ID | DATE | NAME | VALUE_1 | VALUE_2
1 | 27.11.2015 | Homer | A | B
2 | 27.11.2015 | Bart | C | B
3 | 28.11.2015 | Homer | A | C
4 | 28.11.2015 | Maggie | C | B
5 | 28.11.2015 | Bart | C | B
I currently delete duplicate rows (thank to this thread) using this code :
WITH cte AS
(SELECT ROW_NUMBER() OVER (PARTITION BY [VALUE_1], [VALUE_2]
ORDER BY [DATE] DESC) RN
FROM [MY_TABLE])
DELETE FROM cte
WHERE RN > 1
But this code don't delete exactly the lines I want. I would like to delete only rows which values already exist so in my example I would like to delete only line 5 because line 2 have the same values and is older.
Code to create my table and insert values:
CREATE TABLE [t_diff_values]
([id] INT IDENTITY NOT NULL PRIMARY KEY,
[date] DATETIME NOT NULL,
[name] VARCHAR(255) NOT NULL DEFAULT '',
[val1] CHAR(1) NOT NULL DEFAULT '',
[val2] CHAR(1) NOT NULL DEFAULT '');
INSERT INTO [t_diff_values] ([date], [name], [val1], [val2]) VALUES
('2015-11-27','Homer', 'A','B'),
('2015-11-27','Bart', 'C','B'),
('2015-11-28','Homer', 'A','C'),
('2015-11-28','Maggie', 'C','B'),
('2015-11-28','Bart', 'C','B');
You need to add one more CTE where you will index all islands and then apply your duplicate logic in second CTE:
DECLARE #t TABLE
(
ID INT ,
DATE DATE ,
VALUE_1 CHAR(1) ,
VALUE_2 CHAR(1)
)
INSERT INTO #t
VALUES ( 1, '20151127', 'A', 'B' ),
( 2, '20151128', 'C', 'B' ),
( 3, '20151129', 'A', 'B' ),
( 4, '20151130', 'A', 'B' );
WITH cte1
AS ( SELECT * ,
ROW_NUMBER() OVER ( ORDER BY date)
- ROW_NUMBER() OVER ( PARTITION BY VALUE_1, VALUE_2 ORDER BY DATE) AS gr
FROM #t
),
cte2
AS ( SELECT * ,
ROW_NUMBER() OVER ( PARTITION BY VALUE_1, VALUE_2, gr ORDER BY date) AS rn
FROM cte1
)
DELETE FROM cte2
WHERE rn > 1
SELECT *
FROM #t
Try this
CREATE TABLE [dbo].[Employee](
[ID] INT NOT NULL,
[Date] DateTime NOT NULL,
[VAL1] varchar(20) NOT NULL,
[VAL2] varchar(20) NOT NULL
)
INSERT INTO [dbo].[Employee] VALUES
(1,'2015-11-27 10:44:33.087','A','B')
INSERT INTO [dbo].[Employee] VALUES
(2,'2015-11-28 10:44:33.087','C','B')
INSERT INTO [dbo].[Employee] VALUES
(3,'2015-11-29 10:44:33.087','A','B')
INSERT INTO [dbo].[Employee] VALUES
(4,'2015-11-30 10:44:33.087','A','B')
with cte as(
select
*,
rn = row_number() over(partition by [VAL1], [VAL2]
ORDER BY [DATE] DESC),
cc = count(*) over(partition by [VAL1], [VAL2])
from [Employee]
)
delete
from cte
where
rn > 1 and rn < cc
select * from [Employee]
You could use this query:
WITH cte AS
(
SELECT RN = ROW_NUMBER() OVER (ORDER BY ID)
, *
FROM #data
)
DELETE FROM c1
--SELECT *
FROM CTE c1
INNER JOIN CTE c2 ON c1.RN +1 = c2.RN AND c1.VALUE_1 = c2.VALUE_1 AND c1.VALUE_2 = c2.VALUE_2
Here I order them by ID. If the next one (RN+1) has similar V1 and V2, it is deleted.
Output:
ID DATE VALUE_1 VALUE_2
1 2015-11-27 A B
2 2015-11-28 C B
4 2015-11-30 A B
Data:
declare #data table(ID int, [DATE] date, VALUE_1 char(1), VALUE_2 char(1));
insert into #data(ID, [DATE], VALUE_1, VALUE_2) values
(1, '20151127', 'A', 'B'),
(2, '20151128', 'C', 'B'),
(3, '20151129', 'A', 'B'),
(4, '20151130', 'A', 'B');

Merge a two way relation in the same table in SQL Server

Current Data
ID | Name1 | Name2
<guid1> | XMind | MindNode
<guid2> | MindNode | XMind
<guid3> | avast | Hitman Pro
<guid4> | Hitman Pro | avast
<guid5> | PPLive | Hola!
<guid6> | ZenMate | Hola!
<guid7> | Hola! | PPLive
<guid8> | Hola! | ZenMate
Required Output
ID1 | ID2 | Name1 | Name2
<guid1> | <guid2> | XMind | MindNode
<guid3> | <guid4> | avast | Hitman Pro
<guid5> | <guid7> | PPLive | Hola!
<guid6> | <guid8> | Hola! | ZenMate
These are relations between apps. I want to show that Avast and Hitman has a relation but in this view i do not need to show in what "direction" they have an relation. It's a given in this view that the relation goes both ways.
EDIT: Seems like my example was to simple. The solution doesn't work with more data.
DECLARE #a TABLE (ID INT, Name1 VARCHAR(50), Name2 VARCHAR(50))
INSERT INTO #a VALUES ( 1, 'XMind', 'MindNode' )
INSERT INTO #a VALUES ( 2, 'MindNode', 'XMind' )
INSERT INTO #a VALUES ( 3, 'avast', 'Hitman Pro' )
INSERT INTO #a VALUES ( 4, 'Hitman Pro', 'avast' )
INSERT INTO #a VALUES ( 5, 'PPLive Video Accelerator', 'Hola! Better Internet' )
INSERT INTO #a VALUES ( 6, 'ZenMate', 'Hola! Better Internet' )
INSERT INTO #a VALUES ( 7, 'Hola! Better Internet', 'PPLive Video Accelerator' )
INSERT INTO #a VALUES ( 8, 'Hola! Better Internet', 'ZenMate' )
SELECT a1.ID AS ID1 ,
a2.ID AS ID2 ,
a1.Name1 ,
a2.Name1 AS Name2
FROM #a a1
JOIN #a a2 ON a1.Name1 = a2.Name2
AND a1.ID < a2.ID -- avoid duplicates
This works however so i guess it's the Guid that is messing with me.
EDIT AGAIN:
I haven't looked at this for a while and i thought it worked but i just realized it does not. I've struggled all morning with this but i must admit that SQL is not really my strong suite. The thing is this.
DECLARE #a TABLE (ID int, Name1 VARCHAR(50), Name2 VARCHAR(50))
INSERT INTO #a VALUES ( 1, 'XMind', 'MindNode' )
INSERT INTO #a VALUES ( 2, 'MindNode', 'XMind' )
INSERT INTO #a VALUES ( 3, 'avast', 'Hitman Pro' )
INSERT INTO #a VALUES ( 4, 'PPLive Video Accelerator', 'Hola! Better Internet' )
INSERT INTO #a VALUES ( 5, 'ZenMate', 'Hola! Better Internet' )
INSERT INTO #a VALUES ( 6, 'Hitman Pro', 'avast' )
INSERT INTO #a VALUES ( 7, 'Hola! Better Internet', 'PPLive Video Accelerator' )
INSERT INTO #a VALUES ( 8, 'Hola! Better Internet', 'ZenMate' )
INSERT INTO #a VALUES ( 9, 'XX', 'A' )
INSERT INTO #a VALUES ( 10, 'XX', 'BB' )
INSERT INTO #a VALUES ( 11, 'BB', 'XX' )
INSERT INTO #a VALUES ( 12, 'A', 'XX' )
INSERT INTO #a VALUES ( 13, 'XX', 'CC' )
INSERT INTO #a VALUES ( 14, 'CC', 'XX' )
;With CTE as
(
SELECT a1.ID AS ID1 ,
a2.ID AS ID2 ,
a1.Name1 ,
a2.Name1 AS Name2,
CheckSum(Case when a1.Name1>a2.Name1 then a2.Name1+a1.Name1 else a1.Name1+a2.Name1 end) ck, -- just for display
Row_Number() over (Partition by CheckSum(Case when a1.Name1>a2.Name1 then a2.Name1+a1.Name1 else a1.Name1+a2.Name1 end)
order by CheckSum(Case when a1.Name1>a2.Name1 then a2.Name1+a1.Name1 else a1.Name1+a2.Name1 end)) as rn
FROM #a a1
JOIN #a a2 ON a1.Name1 = a2.Name2
)
Select ID1, ID2,Name1, Name2
from CTE C1
where rn=1
When i use this code it sure works fine with the names but it doesn't match the ID's correctly.
The result is
ID1 | ID2 | Name1 | Name2
12 | 9 | A | X (Correct)
7 | 5 | Hola! | ZenMate (Not Correct)
[..]
I've pulled my hair all morning but i can't figure this out. I still use Guid's as ID's and just use Int's here to make it a bit more readable.
DECLARE #a TABLE (ID INT, Name1 VARCHAR(50), Name2 VARCHAR(50))
INSERT INTO #a VALUES ( 1, 'XMind', 'MindNode' )
INSERT INTO #a VALUES ( 2, 'MindNode', 'XMind' )
INSERT INTO #a VALUES ( 3, 'avast', 'Hitman Pro' )
INSERT INTO #a VALUES ( 4, 'Hitman Pro', 'avast' )
SELECT a1.ID AS ID1 ,
a2.ID AS ID2 ,
a1.Name1 ,
a2.Name1 AS Name2
FROM #a a1
JOIN #a a2 ON a1.Name1 = a2.Name2
AND a1.ID < a2.ID -- avoid duplicates
Referring to the amendment and extension of your question, a more complicated solution is required.
We form a CHECKSUM on a1.Name1,a2.Name (to get an identical we exchanged on size).
Using this we generate with ROW_NUMBER (Transact-SQL) a number and use only rows from the result with number 1.
DECLARE #a TABLE (ID uniqueIdentifier, Name1 VARCHAR(50), Name2 VARCHAR(50))
INSERT INTO #a VALUES ( NewID(), 'XMind', 'MindNode' )
INSERT INTO #a VALUES ( NewID(), 'MindNode', 'XMind' )
INSERT INTO #a VALUES ( NewID(), 'avast', 'Hitman Pro' )
INSERT INTO #a VALUES ( NewID(), 'Hitman Pro', 'avast' )
INSERT INTO #a VALUES ( NewID(), 'PPLive Video Accelerator', 'Hola! Better Internet' )
INSERT INTO #a VALUES ( NewID(), 'ZenMate', 'Hola! Better Internet' )
INSERT INTO #a VALUES ( NewID(), 'Hola! Better Internet', 'PPLive Video Accelerator' )
INSERT INTO #a VALUES ( NewID(), 'Hola! Better Internet', 'ZenMate' )
INSERT INTO #a VALUES ( NewID(), 'XX', 'A' )
INSERT INTO #a VALUES ( NewID(), 'A', 'XX' )
INSERT INTO #a VALUES ( NewID(), 'XX', 'BB' )
INSERT INTO #a VALUES ( NewID(), 'BB', 'XX' )
INSERT INTO #a VALUES ( NewID(), 'XX', 'CC' )
INSERT INTO #a VALUES ( NewID(), 'CC', 'XX' )
;With CTE as
(
SELECT a1.ID AS ID1 ,
a2.ID AS ID2 ,
a1.Name1 ,
a2.Name1 AS Name2,
CheckSum(Case when a1.Name1>a2.Name1 then a2.Name1+a1.Name1 else a1.Name1+a2.Name1 end) ck, -- just for display
Row_Number() over (Partition by CheckSum(Case when a1.Name1>a2.Name1 then a2.Name1+a1.Name1 else a1.Name1+a2.Name1 end)
order by CheckSum(Case when a1.Name1>a2.Name1 then a2.Name1+a1.Name1 else a1.Name1+a2.Name1 end)) as rn
FROM #a a1
JOIN #a a2 ON a1.Name1 = a2.Name2
)
Select *
from CTE C1
where rn=1
Edit:
If you only want to get those where both fields are fitting the needed query would simply be:
SELECT a1.ID AS ID1 , a2.ID AS ID2 , a1.Name1 , a2.Name1 AS Name2
FROM #a a1
JOIN #a a2 ON a1.Name1 = a2.Name2 and a1.Name2 = a2.Name1 AND a1.ID < a2.ID
If the output should contain only two-way relations ('XX' + 'A') AND ('A' + 'XX'), try this:
;
WITH m (ID1, ID2, Name1, Name2) AS (
SELECT ID1, ID2, Name1, Name2
FROM (
SELECT a1.ID AS ID1
,a2.ID AS ID2
,a1.Name1 AS Name1
,a2.Name1 AS Name2
,ROW_NUMBER() OVER (PARTITION BY a1.Name1, a2.Name1 ORDER BY (SELECT 1)) AS n
FROM #a AS a1
JOIN #a AS a2
ON a1.Name1 = a2.Name2
AND a1.Name2 = a2.Name1
) AS T
WHERE n = 1
)
SELECT DISTINCT *
FROM (
SELECT ID1, ID2, Name1, Name2
FROM m
WHERE ID1 <= ID2
UNION ALL
SELECT ID2, ID1, Name2, Name1
FROM m
WHERE ID1 > ID2
) AS dm
It produces the output as follows:
+------+-----+--------------------------+-----------------------+
| ID1 | ID2 | Name1 | Name2 |
+------+-----+--------------------------+-----------------------+
| 1 | 2 | XMind | MindNode |
| 3 | 6 | avast | Hitman Pro |
| 4 | 7 | PPLive Video Accelerator | Hola! Better Internet |
| 5 | 8 | ZenMate | Hola! Better Internet |
| 9 | 12 | XX | A |
| 10 | 11 | XX | BB |
| 13 | 14 | XX | CC |
+------+-----+--------------------------+-----------------------+
Just rank your rows with ROW_NUMBER function and use this rank in join instead of original ID column:
DECLARE #a TABLE (ID UNIQUEIDENTIFIER, Name1 VARCHAR(50), Name2 VARCHAR(50))
INSERT INTO #a VALUES ( NEWID(), 'XMind', 'MindNode' )
INSERT INTO #a VALUES ( NEWID(), 'MindNode', 'XMind' )
INSERT INTO #a VALUES ( NEWID(), 'avast', 'Hitman Pro' )
INSERT INTO #a VALUES ( NEWID(), 'Hitman Pro', 'avast' )
INSERT INTO #a VALUES ( NEWID(), 'PPLive Video Accelerator', 'Hola! Better Internet' )
INSERT INTO #a VALUES ( NEWID(), 'ZenMate', 'Hola! Better Internet' )
INSERT INTO #a VALUES ( NEWID(), 'Hola! Better Internet', 'PPLive Video Accelerator' )
INSERT INTO #a VALUES ( NEWID(), 'Hola! Better Internet', 'ZenMate' )
;WITH cte AS(SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) rn FROM #a)
SELECT a1.ID AS ID1 ,
a2.ID AS ID2 ,
a1.Name1 ,
a2.Name1 AS Name2
FROM cte a1
JOIN cte a2 ON a1.Name1 = a2.Name2 AND
a2.Name1 = a1.Name2 AND
a1.rn < a2.rn
Output:
ID1 ID2 Name1 Name2
Guid Guid XMind MindNode
Guid Guid avast Hitman Pro
Guid Guid PPLive Video Accelerator Hola! Better Internet
Guid Guid ZenMate Hola! Better Internet
I suggest you to use this simple way:
SELECT
t2.ID, t3.ID ID2,
t1.Name1,t1.Name2
FROM (
SELECT DISTINCT
CASE WHEN Name1 <= Name2 THEN Name1 ELSE Name2 END AS Name1,
CASE WHEN Name1 <= Name2 THEN Name2 ELSE Name1 END AS Name2
FROM
#a) t1
JOIN
#a t2 ON t1.Name1+t1.Name2 = t2.Name1+t2.Name2
JOIN
#a t3 ON t1.Name1+t1.Name2 = t3.Name2+t3.Name1
For this:
ID | ID2 | Name1 | Name2
----+-----+-----------------------+---------------------------
12 | 9 | A | XX
3 | 4 | avast | Hitman Pro
11 | 10 | BB | XX
14 | 13 | CC | XX
7 | 5 | Hola! Better Internet | PPLive Video Accelerator
8 | 6 | Hola! Better Internet | ZenMate
2 | 1 | MindNode | XMind
You can solve this using a CROSS APPLY
SELECT a2.ID ID_1,a1.ID ID_2, a2.Name1 , a2.Name2
FROM #a a1
CROSS APPLY
(
SELECT ID, Name2, Name1
FROM #a aa
WHERE aa.Name1 = a1.Name2 AND a1.Name1 = aa.Name2 AND a1.ID > aa.ID
) a2
You can try also:
select min(ID) ID1,
max(ID) ID2,
Name1,
Name2
from ( -- Here I get all the IDs and each couple sorted
-- Change > to < if you don't like the order
select ID,
case
when Name1 > Name2 then Name1
else Name2
end Name1,
case
when Name1 > Name2 then Name2
else Name1
end Name2
from table1
) as t
group by Name1,
Name2
You can even tansform this in a simgle query, without the inner one, but I think in this way it's more readable and you can understand better my approach.

How can i remove rows from several tables with SQL?

I have a 3 tables:
mainTable:
id | uniqField
--------------
1 | 1111
2 | 1111
3 | 2222
4 | 2222
5 | 3333
6 | 4444
table1:
id|name|deleted
----------
1|Mike|0
2|Mike|0
5|John|0
table2:
id|name|deleted
----------
3|Peke|0
4|Peke|0
6|Vels|0
That tables bind by id field.
Now i want to remove duplicates from this tables. For mainTable i can use:
DELETE mainTable
FROM mainTable
LEFT OUTER JOIN (
SELECT MIN(id) as RowId, uniqField
FROM mainTable
GROUP BY uniqField
) as KeepRows ON
mainTable.id= KeepRows.RowId
WHERE
KeepRows.RowId IS NULL
But in tables1 and table2 i want to set deleted field to 0->1 to duplicates I mean:
1 | 1111
2 | 1111 -> duplicate->remove
1|Mike|0
2|Mike|0->duplicate-> 0->1
Try this one -
SET NOCOUNT ON;
DECLARE #mainTable TABLE
(
id INT,
uniqField INT
)
INSERT INTO #mainTable (id, uniqField)
VALUES (1, 1111), (2, 1111), (3, 2222), (4, 2222), (5, 3333), (6, 4444)
DECLARE #deleted TABLE (id INT)
;WITH cte AS
(
SELECT *, RowNum = ROW_NUMBER() OVER (PARTITION BY uniqField ORDER BY 1/0)
FROM #mainTable
)
DELETE FROM cte
OUTPUT DELETED.id INTO #deleted
WHERE RowNum > 1
DECLARE #table1 TABLE
(
id INT,
uniqField VARCHAR(10),
deleted BIT
)
DECLARE #table2 TABLE
(
id INT,
uniqField VARCHAR(10),
deleted BIT
)
INSERT INTO #table1 (id, uniqField, deleted)
VALUES (1, 'Mike', 0), (2, 'Mike', 0), (5, 'John', 0)
INSERT INTO #table2 (id, uniqField, deleted)
VALUES (3, 'Peke', 0), (4, 'Peke', 0), (6, 'Vels', 0)
UPDATE #table1
SET deleted = 1
FROM #deleted
WHERE [#deleted].id = [#table1].id
UPDATE #table2
SET deleted = 1
FROM #deleted
WHERE [#deleted].id = [#table2].id
SELECT * FROM #table1
SELECT * FROM #table2
Output -
id uniqField deleted
----------- ---------- -------
1 Mike 0
2 Mike 1
5 John 0
id uniqField deleted
----------- ---------- -------
3 Peke 0
4 Peke 1
6 Vels 0