SQL Relations many to many giving one element only - sql

I have a SQL table that is made up of two columns without column id the two columns is combining of equivalent parts of cars in which it is necessary to return in one column all possible relations giving an element of one column or another, for example the given value is 530:
ColunaA ColunaB
530 520
530 510
530 444
444 222
333 111
The end result has to be:
Column X
530
520
510
444
222
I tried this SQL query:
WITH AreasCTE AS
(
SELECT colunaa, colunab FROM dbo.Tabela1 WHERE colunaa = '530'
UNION ALL
SELECT a.colunaa, a.colunab FROM dbo.Tabela1 a
INNER JOIN AreasCTE s ON a.colunaa = s.colunaa
)
SELECT top 100 colunaa, colunab FROM AreasCTE
Thanks for the help!!

Try this, it's not perfect solution probably needs some performance tuning but should do the job.
DECLARE #t1 TABLE
(
colunaA NVARCHAR(MAX) ,
colunaB NVARCHAR(MAX)
);
INSERT INTO #t1
VALUES ( 530, 520 ) ,
( 530, 510 ) ,
( 530, 444 ) ,
( 444, 222 ) ,
( 333, 111 );
SELECT *
FROM #t1;
DECLARE #value NVARCHAR(MAX);
SET #value = 222;
DECLARE #filter TABLE
(
id NVARCHAR(MAX)
);
INSERT INTO #filter
VALUES ( #value );
DECLARE #right_row_count INT;
DECLARE #left_row_count INT;
SET #left_row_count = 1;
WHILE #left_row_count > 0
OR #right_row_count > 0
BEGIN
INSERT INTO #filter ( id )
SELECT DISTINCT t.colunaA
FROM #t1 t
INNER JOIN #filter f ON t.colunaB = f.id
WHERE t.colunaA NOT IN ( SELECT *
FROM #filter );
SET #right_row_count = ##rowcount;
INSERT INTO #filter ( id )
SELECT DISTINCT t.colunaB
FROM #t1 t
INNER JOIN #filter f ON t.colunaA = f.id
WHERE t.colunaB NOT IN ( SELECT *
FROM #filter );
SET #left_row_count = ##rowcount;
END;
SELECT *
FROM #filter
ORDER BY id DESC;

Related

How to insert records of the range in table when I define the range in first table in SQL Server 2012

Here I have two table, with the name Table A and Table B.
Table A:
ID From To
-------------------
1 985 992
2 1201 1207
3 1584 1589
Table B:
ID Numbers
---------------------------
1 985
2 986
3 987
4 988
5 989
6 990
7 991
8 992
9 1201
10 1202
11 1203
12 1204
13 1205
14 1206
and the number goes like this. And the table structure as well.
How can such kind of data can be insert. As I define range from 125- 135 in table A, all the number with in this range must be inserted at table B.
Thanks to all the well wisher for their valuable suggestion. Answer has been solve with using trigger.
CREATE TRIGGER trgAfterInsert on samplea
FOR INSERT
AS declare #id int, #from bigint, #to bigint, #number bigint;
select #id=i.id from inserted i;
select #from=i.fromnum from inserted i;
select #to=i.tonum from inserted i;
set #number=#from
while #number<=#to
begin
insert into sampleB (id, numbers) values (#id,#number);
set #number=#number+1
end
Finally the problem is solved. With this inserting data range in table A, data will be automatically inserted in table B with this trigger.
You can do it with a cursor and while loops,
DELCARE #Uid int, #Ustart int, #Uend int, #Ucounter;
DECLARE Ucursor CURSOR
FOR SELECT * FROM TableA ;
OPEN vend_cursor
FETCH NEXT FROM Ucursor
INTO #Uid,#Ustart,#Uend
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Ucounter = #Ustart
WHILE #Ucounter <> #Uend
BEGIN
INSERT INTO TableB
VALUES (#Ucount) -- Set the identity on for id
SET #Ucounter += 1
END
FETCH NEXT FROM Ucursor
INTO #Uid,#Ustart,#Uend
END
CLOSE Ucursor;
Not sure if this is efficient but it works.
DECLARE #range INT = (SELECT [To] - [From] FROM #tableA WHERE [Id] = 1)
DECLARE #count INT = 0
WHILE (#count <= #range)
BEGIN
INSERT INTO #tableB
SELECT [From] + #count FROM #tableA
SET #count = #count + 1
END
I would suggest a recursive CTE:
with cte as (
select from as n, from, to
from a
union all
select n + 1, from, to
from cte
where n < to
)
select n
from cte;
To create a table, you can do:
with cte as (
select from as n, from, to
from a
union all
select n + 1, from, to
from cte
where n < to
)
select identity(), n
into b
from cte;
Notes:
I left the column names as you have them without escaping them. Obviously, from and to are keywords in SQL.
If you have a gap of more than 100, you'll want to use the MAXRECURSION option.
You can insert values just as easily as creating a new table.
Try this,
declare #t table(ID int,Froms int,Tos int)
insert into #t values
(1 , 985 , 992 )
,(2 , 1201 , 1207 )
,(3 , 1584 , 1589 )
declare #table2 table(id int identity(1,1),numbers int)
insert into #table2
select number from #t t
cross apply(
select distinct number from master..spt_values
where number>t.[froms] and number<=t.tos)ca
select * from #table2

How to traverse a path in a table with id & parentId?

Suppose I have a table like:
id | parentId | name
1 NULL A
2 1 B
3 2 C
4 1 E
5 3 E
I am trying to write a scalar function I can call as:
SELECT dbo.GetId('A/B/C/E') which would produce "5" if we use the above reference table. The function would do the following steps:
Find the ID of 'A' which is 1
Find the ID of 'B' whose parent is 'A' (id:1) which would be id:2
Find the ID of 'C' whose parent is 'B' (id:2) which would be id:3
Find the ID of 'E' whose parent is 'C' (id:3) which would be id:5
I was trying to do it with a WHILE loop but it was getting very complicated very fast... Just thinking there must be a simple way to do this.
CTE version is not optimized way to get the hierarchical data. (Refer MSDN Blog)
You should do something like as mentioned below. It's tested for 10 millions of records and is 300 times faster than CTE version :)
Declare #table table(Id int, ParentId int, Name varchar(10))
insert into #table values(1,NULL,'A')
insert into #table values(2,1,'B')
insert into #table values(3,2,'C')
insert into #table values(4,1,'E')
insert into #table values(5,3,'E')
DECLARE #Counter tinyint = 0;
IF OBJECT_ID('TEMPDB..#ITEM') IS NOT NULL
DROP TABLE #ITEM
CREATE TABLE #ITEM
(
ID int not null
,ParentID int
,Name VARCHAR(MAX)
,lvl int not null
,RootID int not null
)
INSERT INTO #ITEM
(ID,lvl,ParentID,Name,RootID)
SELECT Id
,0 AS LVL
,ParentId
,Name
,Id AS RootID
FROM
#table
WHERE
ISNULL(ParentId,-1) = -1
WHILE ##ROWCOUNT > 0
BEGIN
SET #Counter += 1
insert into #ITEM(ID,ParentId,Name,lvl,RootID)
SELECT ci.ID
,ci.ParentId
,ci.Name
,#Counter as cntr
,ch.RootID
FROM
#table AS ci
INNER JOIN
#ITEM AS pr
ON
CI.ParentId=PR.ID
LEFT OUTER JOIN
#ITEM AS ch
ON ch.ID=pr.ID
WHERE
ISNULL(ci.ParentId, -1) > 0
AND PR.lvl = #Counter - 1
END
select * from #ITEM
Here is an example of functional rcte based on your sample data and requirements as I understand them.
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
create table #Something
(
id int
, parentId int
, name char(1)
)
insert #Something
select 1, NULL, 'A' union all
select 2, 1, 'B' union all
select 3, 2, 'C' union all
select 4, 1, 'E' union all
select 5, 3, 'E'
declare #Root char(1) = 'A';
with MyData as
(
select *
from #Something
where name = #Root
union all
select s.*
from #Something s
join MyData d on d.id = s.parentId
)
select *
from MyData
Note that if you change the value of your variable the output will adjust. I would make this an inline table valued function.
I think I have it based on #SeanLange's recommendation to use a recursive CTE (above in the comments):
CREATE FUNCTION GetID
(
#path VARCHAR(MAX)
)
/* TEST:
SELECT dbo.GetID('A/B/C/E')
*/
RETURNS INT
AS
BEGIN
DECLARE #ID INT;
WITH cte AS (
SELECT p.id ,
p.parentId ,
CAST(p.name AS VARCHAR(MAX)) AS name
FROM tblT p
WHERE parentId IS NULL
UNION ALL
SELECT p.id ,
p.parentId ,
CAST(pcte.name + '/' + p.name AS VARCHAR(MAX)) AS name
FROM dbo.tblT p
INNER JOIN cte pcte ON
pcte.id = p.parentId
)
SELECT #ID = id
FROM cte
WHERE name = #path
RETURN #ID
END

How to pass multiple integer values in one parameter in SQL Server

I am in the process of producing a report in SSRS and trying to add a check in to say when this parameter equals this value then display these values, but because the parameter is an integer and I can't pass multiple integer values into the one parameter.
How can I do this?
Here is example of what I am trying to do:
DECLARE #EntityGroupID INT
SET #EntityGroupID = 741
IF #EntityGroupID = 741
BEGIN
SET #EntityGroupID= 3097,3098,3099,3100,3101,3125
END
SELECT *
FROM tEntityGroup
WHERE ID in (#EntityGroupID)
Why not just use two SELECT statements. If you already have your control flow, just use it to do the SELECT, e.g.
DECLARE #EntityGroupID INT
SET #EntityGroupID = 741
IF #EntityGroupID = 741
BEGIN
SELECT *
FROM tEntityGroup
WHERE ID IN (3097,3098,3099,3100,3101,3125);
END
ELSE
BEGIN
SELECT *
FROM tEntityGroup
WHERE ID = #EntityGroupID;
END
Or if you really want to do it with a single select, then use a table variable:
DECLARE #EntityGroupID INT
SET #EntityGroupID = 741
DECLARE #Entities TABLE (ID INT NOT NULL);
INSERT #Entities (ID)
SELECT #EntityGroupID
WHERE #EntityGroupID != 741
UNION ALL
SELECT ID
FROM (VALUES (3097),(3098),(3099),(3100),(3101),(3125)) t (ID)
WHERE #EntityGroupID = 741;
SELECT *
FROM tEntityGroup
WHERE ID in (SELECT ID FROM #Entities);
Pass it as table variable like
declare #tbl table(EntityGroupID int);
insert into #tbl
select 3097
union
select 3098
union
select 3099
Then you can just say
SELECT *
FROM tEntityGroup
WHERE ID in (select EntityGroupID from #tbl);
You can first create a table variable with ID,Value pair as:
DECLARE #EntityGroup TABLE(Id INT, VALUE VARCHAR(50))
INSERT INTO #EntityGroup VALUES (741,null)
IF (SELECT Id FROM #EntityGroup) = 741
BEGIN
update #EntityGroup SET VALUE = '3097,3098,3099,3100,3101,3125' WHERE Id = 741
END
and then we can extract the data as required:
SELECT *
FROM tEntityGroup
WHERE ID in
(
SELECT LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) AS EntityGroupID
FROM
(
SELECT CAST('<XMLRoot><RowData>' + REPLACE(VALUE,',','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) AS x
FROM #EntityGroup
)t
CROSS APPLY x.nodes('/XMLRoot/RowData')m(n)
)

sql where statement with multiple values

In my WHERE statement I'm trying to find some entities the have BOTH conditions, I cannot use the IN statement because it works like OR and returning only one result of many, means if I am using:
WHERE R.RiskID IN(221,111)
So I will get the Documents that have or 111 OR 222
its a problem for me to use the AND statement because the proc is dynamic and generates complex code.
example:
WHERE R.RiskID IN(111,222)
Again, it works like OR statement. The defenition table have a row for each RiskID, means if I have many RiskIDs for same document it looks like:
RiskID DocumentID
111 345
222 345
333 345
999 846
111 846
my final destinations should be, if my input is 111,222 I need to show all the Documents that have 111 AND 222 RiskIDs.
if anybody have the same problem so the answer is COMBINATIONS and PERMUTATIONS...
;with list (n) as
(
select *
from
(
values
('a'),
('b'),
('c'),
('d'),
('e')
) x(y)
), cte as
(
select CAST( n AS NVARCHAR(MAX) ) n
from list c
union all
select CAST( l.n + ',' + c.n AS NVARCHAR(MAX) )
from cte c, list l
where l.n > c.n
)
select *
from cte
the generator generates all the possible combinations for the VALUES that in the FROM clouse.
Overkill for the win. Use this script for smaller query or indexed query.
I have put in the two option, one is to manually define the Riskid or uncomment the sql query to do a wildcard or enter in base on a selection.
Create table #Risk (RID Int Identity,RiskID int)
/* Manual Insert */
insert into #Risk select 111
insert into #Risk select 222
/* SQL insert */
/*
insert into #Risk select distinct RiskID from Tablename where RiskID in (111,222)
*/
Declare #Loop int
set #Loop = 1
Create table #final (RiskID int, DocumentID int)
while #Loop < (select MAX(rid)+1 from #Risk)
Begin
insert into #final
select Riskid, DocumentID
from tablename
where riskid = (select riskid from #Risk where RID = #Loop)
set #Loop = #Loop + 1
End

Is it possible to group unlike items and count them together?

Given the following table:
Chain Name
123 Company 1
124 Other Company 1
123 Whatever Company
125 This One
126 That One
125 Another One
127 Last One
I get the following results when I do a Count on the Chain column:
123 2
124 1
125 2
126 1
127 1
Is it possible to group Chain 123 and 124 so they're counted together? Also group 125 and 126? The modified results would look like this:
123/124 3
125/126 3
127 1
My SQL looks like this:
SELECT Table1.Chain, Count(*) as [Count]
FROM Table1 LEFT JOIN Table2 on Table1.Chain = Table2.Chain
WHERE (((Table1.Chain) IN (Table2.Chain)))
GROUP BY Table1.Chain
ORDER BY Table1.Chain;
Thank you!
Depending upon your needs, this might be a bit of a hack, but I would probably add a table to store the Chain and ChainGroup that you are seeking. Something like this:
Chain ChainGroup
123 123/124
124 123/124
125 125/126
126 125/126
127 127/128
Then, in the query, I would join to this table and instead of grouping by Chain I would group by ChainGroup.
I would prefer this over something like a nested IIF statement as those get pretty difficult to debug, and odds are you'll have additional groupings in the future which would be trivial to add to the table and have the new grouping automatically appear in the query.
yes, you can:
SELECT min(Table1.Chain) & '/' & max(Table1.Chain) as chain, Count(*) as [Count]
FROM Table1 LEFT JOIN Table2 on Table1.Chain = Table2.Chain
WHERE (((Table1.Chain) IN (Table2.Chain)))
GROUP BY int((Table1.Chain-1)/2)
ORDER BY min(Table1.Chain);
You can use a nested Iif statement. Hopefully I've got all my parentheses right below! :-)
SELECT Iif(Table1.Chain="123", "123/124",
Iif(Table1.Chain="124", "123/124",
Iif(Table1.Chain="125", "125/126",
Iif(Table1.Chain="126", "125/126", Table1.Chain)))) as [Chain]
, Count(*) as [Count]
FROM Table1 LEFT JOIN Table2 on Table1.Chain = Table2.Chain
WHERE (((Table1.Chain) IN (Table2.Chain)))
GROUP BY Iif(Table1.Chain="123", "123/124",
Iif(Table1.Chain="124", "123/124",
Iif(Table1.Chain="125", "125/126",
Iif(Table1.Chain="126", "125/126", Table1.Chain))))
ORDER BY Table1.Chain;
You could also move the case statement into a subquery in your from clause or a common table expression if you don't want to write it twice in your query.
consider something like:
SELECT
chain_group, COUNT(*) FROM (
SELECT
Table1.Chain,
switch(Table1.Chain IN("123","124"), "123/124",
Table1.Chain IN("125","126"),"125/126",
Table1.Chain) AS chain_group
FROM
Table1 INNER JOIN
Table2 ON
Table1.Chain = Table2.Chain) t
GROUP BY chain_group
ORDER BY chain_group
You can see the below example
----- Make main Table
CREATE TABLE #test
( id int , Name varchar(100))
INSERT #test(id,Name)
values (123,'Company 1'),
(124,'Other Company 1'),
(123, 'Whatever Company'),
(125, 'This One'),
(126 , 'That One'),
(125, 'Another One'),
(127, 'Last One')
CREATE TABLE #temp
(rowID INT IDENTITY(1,1) , ID INT ,cnt INT )
CREATE TABLE #tempResult
(ID VARCHAR(20) ,cnt INT )
INSERT INTO #temp(ID,cnt)
SELECT ID ,COUNT(1) cnt FROM #test GROUP BY ID
DECLARE #rowCnt INT , #TotalCnt INT , #even INT , #odd INT ,
#idNum VARCHAR(20) , #valueCnt INT , #inStart INT = 1
SET #rowCnt = 1
SET #even = 1
SET #odd = 2
SELECT #TotalCnt = COUNT(1) FROM #temp
WHILE #rowCnt <= #TotalCnt
BEGIN
SET #inStart = 1
SET #odd = #rowCnt
SET #even = #rowCnt + 1
SET #idNum = ''
SET #valueCnt = 0
WHILE #inStart <= 2
BEGIN
IF #inStart = 1
Begin
SELECT #idNum = Convert(VARCHAR(5),ID) , #valueCnt = cnt
FROM #temp WHERE rowID = #odd
End
ELSE
BEGIN
SELECT #idNum = #idNum + '/' + Convert(VARCHAR(5),ID) , #valueCnt = #valueCnt + cnt
FROM #temp WHERE rowID = #even
END
SET #inStart = #inStart + 1
END
INSERT INTO #tempResult (ID, Cnt)
VALUES (#idNum,#valueCnt)
SET #rowCnt = #rowCnt + 2
END
SELECT *
FROM #tempResult