Recursive SQL Query - sql

First of all thanks for the help , I'm no expert on sql and maybe do a very basic question
my database is SQL Server 2012
I have a table with the fields shown below
servicerequestareaid - enumtypeid -value -ordinal - parentid
69 - D115C073-613F-F8C4-04A1-DE63B28D2496 - Servidor Exchange - 0 - 68
70 - 3A09DA42-B33E-05EA-C2CB-167FECBDE346 - Rol CAS - 0 - 69
71 - E9AFC8C0-76F3-2B92-38A3-7A5B0F9FCD07 - Perimetral - 0 - 70
72 - 925F6D8C-EA24-798F-3D6C-EB64AC436D6F - Asegurado - 0 - 71
I need to take the value of servicerequestareaId , enumtypeId and parentId field for that record
i begin with a value of servicerequestareaid,
parentid value corresponds to the value stored in the servicerequestareaid field to another record
this process is repeated until the value stored in parentId is equal to 1
after having that set of values ​​stored must display
create two cursors for this purpose but do not want to do it that way , should habrer a more efficient way to do
![Cursor][2]
thanks for the help

Do it in recursive cte:
DECLARE #t TABLE(id INT, parent_id int)
INSERT INTO #t VALUES
(1, null),
(2, null),
(3, 1),
(4, 3),
(5, 2),
(6, 2),
(7, 5)
DECLARE #id INT = 7
;WITH cte AS(
SELECT * FROM #t WHERE id = #id
UNION ALL
SELECT t.* FROM #t t JOIN cte ON t.id = cte.parent_id
)
SELECT * FROM cte
Output:
id parent_id
7 5
5 2
2 NULL
I think you will easily adjust this to your tables.

Related

SQL Server Query to fetch nested data

I have a table like this -
declare #tmpData as table
(
MainId int,
RefId int
)
INSERT INTO #tmpData
(MainId,
RefId)
VALUES (1, NULL),
(2, 1),
(3, 2),
(4, 3),
(5, NULL),
(6, 5);
SO, If I pass a value for example - 1
then it should return all rows where value 1 is linked directly or indirectly.
And result should be - (Here 1 is ref with MainId 2, and 2 is ref with Main Id 3 and so on...) MaiId 5 and 6 is not related to 1 so output is -
Any one please provide sql server query for the same. Thanks
I tried by applying left join with same table on MainId and RefId.
But not got desired output.
You need a recursive CTE (dbfiddle)
WITH R
AS (SELECT t.MainId,
t.RefId
FROM #tmpData t
WHERE t.MainId = 1
UNION ALL
SELECT t.MainId,
t.RefId
FROM #tmpData t
JOIN R
ON t.RefId = r.MainId)
SELECT *
FROM R

Find data by multiple Lookup table clauses

declare #Character table (id int, [name] varchar(12));
insert into #Character (id, [name])
values
(1, 'tom'),
(2, 'jerry'),
(3, 'dog');
declare #NameToCharacter table (id int, nameId int, characterId int);
insert into #NameToCharacter (id, nameId, characterId)
values
(1, 1, 1),
(2, 1, 3),
(3, 1, 2),
(4, 2, 1);
The Name Table has more than just 1,2,3 and the list to parse on is dynamic
NameTable
id | name
----------
1 foo
2 bar
3 steak
CharacterTable
id | name
---------
1 tom
2 jerry
3 dog
NameToCharacterTable
id | nameId | characterId
1 1 1
2 1 3
3 1 2
4 2 1
I am looking for a query that will return a character that has two names. For example
With the above data only "tom" will be returned.
SELECT *
FROM nameToCharacterTable
WHERE nameId in (1,2)
The in clause will return every row that has a 1 or a 3. I want to only return the rows that have both a 1 and a 3.
I am stumped I have tried everything I know and do not want to resort to dynamic SQL. Any help would be great
The 1,3 in this example will be a dynamic list of integers. for example it could be 1,3,4,5,.....
Filter out a count of how many times the Character appears in the CharacterToName table matching the list you are providing (which I have assumed you can convert into a table variable or temp table) e.g.
declare #Character table (id int, [name] varchar(12));
insert into #Character (id, [name])
values
(1, 'tom'),
(2, 'jerry'),
(3, 'dog');
declare #NameToCharacter table (id int, nameId int, characterId int);
insert into #NameToCharacter (id, nameId, characterId)
values
(1, 1, 1),
(2, 1, 3),
(3, 1, 2),
(4, 2, 1);
declare #RequiredNames table (nameId int);
insert into #RequiredNames (nameId)
values
(1),
(2);
select *
from #Character C
where (
select count(*)
from #NameToCharacter NC
where NC.characterId = c.id
and NC.nameId in (select nameId from #RequiredNames)
) = 2;
Returns:
id
name
1
tom
Note: Providing DDL+DML as shown here makes it much easier for people to assist you.
This is classic Relational Division With Remainder.
There are a number of different solutions. #DaleK has given you an excellent one: inner-join everything, then check that each set has the right amount. This is normally the fastest solution.
If you want to ensure it works with a dynamic amount of rows, just change the last line to
) = (SELECT COUNT(*) FROM #RequiredNames);
Two other common solutions exist.
Left-join and check that all rows were joined
SELECT *
FROM #Character c
WHERE EXISTS (SELECT 1
FROM #RequiredNames rn
LEFT JOIN #NameToCharacter nc ON nc.nameId = rn.nameId AND nc.characterId = c.id
HAVING COUNT(*) = COUNT(nc.nameId) -- all rows are joined
);
Double anti-join, in other words: there are no "required" that are "not in the set"
SELECT *
FROM #Character c
WHERE NOT EXISTS (SELECT 1
FROM #RequiredNames rn
WHERE NOT EXISTS (SELECT 1
FROM #NameToCharacter nc
WHERE nc.nameId = rn.nameId AND nc.characterId = c.id
)
);
A variation on the one from the other answer uses a windowed aggregate instead of a subquery. I don't think this is performant, but it may have uses in certain cases.
SELECT *
FROM #Character c
WHERE EXISTS (SELECT 1
FROM (
SELECT *, COUNT(*) OVER () AS cnt
FROM #RequiredNames
) rn
JOIN #NameToCharacter nc ON nc.nameId = rn.nameId AND nc.characterId = c.id
HAVING COUNT(*) = MIN(rn.cnt)
);
db<>fiddle

Write a function or regular expression that will split string in sql

i have in sql table values in this way:
Id GameId GameSupplierId
1 1 NULL
2 2 NULL
3 3 1
4 3 2
5 3 3
What i want is to filter in sql procedure by GameId and if there is GameSupplierId by supplier too. I will get string from my web page in format GameID ; GameSupplierId. For example:
1; NULL
2; NULL
or if there is GameSupplier too
3;1
3;1,2
Also i want to have multiple choice for example like this:
1,2,3;1,2
In my sql query i will then filter like WHERE #GameID = Table.GameID (and also to check #GameSupplierId IN (,,,))
Just add your desired columns into ORDER BY:
ORDER BY t.GameId, t.GameSuplierId
For example:
DECLARE #table TABLE
(
ID INT,
GameId INT,
GameSuplierId INT NULL
)
INSERT INTO #table
(
ID,
GameId,
GameSuplierId
)
VALUES
(1, 1, NULL)
, (2, 2, NULL)
, (3, 3, 1)
, (4, 3, 2)
, (5, 3, 3)
SELECT
*
FROM #table t
ORDER BY t.GameId, t.GameSuplierId

multiply rows in t-sql

I have following table
ID Number
----------------
1 41.5
2 42.5
3 43.5
2 44.5
2 45.5
1 46.5
1 47.5
I need to write a query which will return distinct ID's and corresponding Number column values multiplied. For the given table result should be like this
ID Result
-----------------
1 41.5 * 46.5 * 47.5
2 42.5 * 44.5 * 45.5
3 etc...
(without use cursors)
SELECT Id, EXP(SUM(LOG(Number))) as Result
FROM Scores
GROUP BY id
This will work for positive numbers, to multiply negative numbers as well you can use ABS() function to use absolute (positive) value but final result will be positive rather than negative number:
SELECT Id, EXP(SUM(LOG(ABS(Number)))) as Result
FROM Scores
GROUP BY id
EDIT: Added test script
DECLARE #data TABLE(id int, number float)
INSERT INTO #data VALUES
(1, 2.2),
(1, 10),
(2, -5.5),
(2, 10)
SELECT Id, EXP(SUM(LOG(ABS(Number)))) as Result
FROM #data GROUP BY id
Output:
1 22
2 55
select id, power(sum(log10(num)),10) group by id
This is a slight variation on row concatenation and Jeff Moden has an excellent article on that at SQL Server Central titled Performance Tuning: Concatenation Functions and Some Tuning Myths
Edit: #mellamokb It is analogous to concatenation, but requires some modification. A sample script would be
create table testMult (id int, num int)
GO
insert into testMult values (1, 2)
insert into testMult values (1, 3)
insert into testMult values (1, 4)
insert into testMult values (2, 2)
GO
create function dbo.fnMult (#someId int)
returns int as
begin
declare #return int
set #return = 1
select #return = #return * num
from testMult
where id = #someId
return #return
end
GO
select *
from testMult
select t1.id,
dbo.fnMult(t1.id)
from testMult t1
group by t1.id
Which is just a very small variation on the script provided by Jeff Moden in his article.

Recursive select in SQL

I have an issue I just can't get my head around. I know what I want, just simply can't get it out on the screen.
What I have is a table looking like this:
Id, PK UniqueIdentifier, NotNull
Name, nvarchar(255), NotNull
ParentId, UniqueIdentifier, Null
ParentId have a FK to Id.
What I want to accomplish is to get a flat list of all the id's below the Id I pass in.
example:
1 TestName1 NULL
2 TestName2 1
3 TestName3 2
4 TestName4 NULL
5 TestName5 1
The tree would look like this:
-1
-> -2
-> -3
-> -5
-4
If I now ask for 4, I would only get 4 back, but if I ask for 1 I would get 1, 2, 3 and 5.
If I ask for 2, I would get 2 and 3 and so on.
Is there anyone who can point me in the right direction. My brain is fried so I appreciate all help I can get.
declare #T table(
Id int primary key,
Name nvarchar(255) not null,
ParentId int)
insert into #T values
(1, 'TestName1', NULL),
(2, 'TestName2', 1),
(3, 'TestName3', 2),
(4, 'TestName4', NULL),
(5, 'TestName5', 1)
declare #Id int = 1
;with cte as
(
select T.*
from #T as T
where T.Id = #Id
union all
select T.*
from #T as T
inner join cte as C
on T.ParentId = C.Id
)
select *
from cte
Result
Id Name ParentId
----------- -------------------- -----------
1 TestName1 NULL
2 TestName2 1
5 TestName5 1
3 TestName3 2
Here's a working example:
declare #t table (id int, name nvarchar(255), ParentID int)
insert #t values
(1, 'TestName1', NULL),
(2, 'TestName2', 1 ),
(3, 'TestName3', 2 ),
(4, 'TestName4', NULL),
(5, 'TestName5', 1 );
; with rec as
(
select t.name
, t.id as baseid
, t.id
, t.parentid
from #t t
union all
select t.name
, r.baseid
, t.id
, t.parentid
from rec r
join #t t
on t.ParentID = r.id
)
select *
from rec
where baseid = 1
You can filter on baseid, which contains the start of the tree you're querying for.
Try this:
WITH RecQry AS
(
SELECT *
FROM MyTable
UNION ALL
SELECT a.*
FROM MyTable a INNER JOIN RecQry b
ON a.ParentID = b.Id
)
SELECT *
FROM RecQry
Here is a good article about Hierarchy ID models. It goes right from the start of the data right through to the query designs.
Also, you could use a Recursive Query using a Common Table Expression.
I'm guessing that the easiest way to accomplish what you're looking for would be to write a recursive query using a Common Table Expression:
MSDN - Recursive Queries Using Common Table Expressions