Select query to reference tables based on table Id or Name - sql

Consider I have a table which list all the tables in database along with the referenced tables. In my case, no foreign key reference are used in the tables. Tables references are maintained as below
TableId ReferedInTableId
1 2
1 3
1 4
2 5
2 6
3 7
4 8
4 9
5 -
6 -
7 10
8 -
9 11
10 -
11 -
In this case, I need a query to find the referenced tables based on the input TableId.
For Eg, for TableId 1, the referencedTableId are 2,3,4. But I need to recurse again like these 2,3,4 are again referred in some tables and I need that list too.
At the end,
If the input is TableId 1, It should return 2,3,4,5,6,7,8,9,10,11
If the input is tableId 4, It should return 8,9,11
If the input is tableId 3, it should return 7,10
Please help me in building a SQL select query.

Assuming you have no cycles in the links, you can use a relatively simple recursive CTE:
with cte as (
select t.tableid, t.referencedtableid, 1 as lev
from t
where t.id = 1
union all
select cte.tableid, t.referencedtableid, lev + 1
from cte join
t
on cte.referencedtableid = t.id
)
select referencedtableid
from cte;

Related

How to get hierarchy children based on a parent id in a SQL table?

I am using postgresql for laravel api. I have users table that contain data like below,
id parent_id
2
7 2
3 2
14 3
5 3
6 3
17 3
4 3
I face difficulties to query the data, if I have parameters parent_id = 2, then the result data should be like this,
id
7
3
14
5
6
17
4
I had been tried this SQL code
WITH q AS
(
SELECT *
FROM users
WHERE parent_id = 2
UNION ALL
SELECT m.*
FROM users as m
JOIN q
ON m.parent_id = q.id
)
SELECT *
FROM q
But it showed error
SQL Error [42P01]: ERROR: relation "q" does not exist¶ Detail: There is a WITH item named "q", but it cannot be referenced from this part of the query.¶ Hint: Use WITH RECURSIVE, or re-order the WITH items to remove forward references.¶ Position: 269
I don't know what's the problem is,
Can somebody help me to solve this problem?
Any help would be appreciate ! Thanks!
try like below
select id from users where parent_id=2
union
select id from users
where parent_id in ( select id from table where parentid=2)

SQL Query to find which group does not have a given value

I am using T-SQL.
Say if I have the following
Value Nbr
----- ---
one 6
one 7
one 8
two 6
two 7
three 5
three 3
three 2
In the above table, I need to find which group does not have 6 in it.
In this case, it is three as it does not have 6 in it.
What would be the best approach to do this?
I tried:
select Value from tbl1
where nbr <> 6
group by Value
but did not get the intended result.
select distinct value
from tbl1
where value not in
(
select distinct value
from tbl1
where nbr = 6
)

SQL - how to return rows containing ALL children rows

Given a parent + reference tables where Reference table is as follows
Ref_ID PARENT_ID
-------------------
1 1
2 1
1 2
3 2
1 3
3 3
4 3
2 4
3 4
I'm trying to return all distinct parent rows where ref_id contains both 2 & 3
The query
SELECT *
FROM Parent
WHERE parent_id in (SELECT parent_id from XRefTable where ref_id in (2, 3) )
returns all parent_id 1, 2, 3, 4
WHEREAS the correct result required is to return parent_id 4 which has BOTH ref_id's 2 & 3, others have EITHER 2 OR 3
Any help is appreciated
FYI - there are 4-7 tables in the query (depending on user selections) so performance is a huge factor
SORRY cannot use stored procedures as it has to work on SQL Server CE too
SELECT parent_id
from XRefTable
where ref_id in ( 2, 3 )
group by PARENT_ID
having count(distinct ref_id) = 2
You could do this:
SELECT
ParentReference.Parent_ID
FROM
ParentReference
INNER JOIN ParentReference B ON ParentReference.Parent_ID = B.Parent_ID AND ParentReference.Ref_ID = 2 AND B.Ref_ID = 3
You are trying to do a set-wise comparison. For this, I strongly recommend the group by and having clauses:
select parent_id
from Reference r
group by parent_id
having sum(case when ref_id = 2 then 1 else 0 end) > 0 and
sum(case when ref_id = 3 then 1 else 0 end) > 0
Each component of the having clause is counting one of the fields. The logic requires that both are present.
The reason that I prefer this approach over others is because you can change the logic, using essentially the same structure.
If you have the list in a comma delimited string, the following will work. Perhaps not "elegant" and "relational", but it works:
set #Ref_ids = "1,2,3,4"
select parent_id
from Reference r
where charindex(','+cast(ref_id as varchar(255))+',', '+#ref_ids+',') > 0
group by parent_id
having count(distinct ref_id) = (len(replace(#ref_ids, ',', '')) - len(#ref_ids))+1
This is doing string manipulation to determine whether the ref_id is in the list. The having clause then counts the number of matches, making sure that it is the same size as the list. This will work, assuming there are no spaces in the list and no blank values.

Select data if conditions on two separate rows are met

Consider the following dataset:
id dataid data
1 3095 5
1 3096 9
1 3097 8
2 3095 4
2 3096 9
2 3097 15
Now, in this, the column someid identifies to certain data, so if I see 3095, I know what data the data column represents (name, address, etc.). I need to do a check so that for the group of ids (i.e. 1 and 2) dataid=3095 then data=5 AND dataid=3096 then data=9, and if this is true, the id group will be selected and operations will be done on it.
Edit: Now I use the following SQL query to do the above:
SELECT *
FROM table s0
JOIN table s1 USING (dataid)
JOIN table s2 USING (dataid)
WHERE s1.dataid=359 AND s1.data=5
AND s2.dataid=360 AND s2.data=6;
But how can I get the output from rows to columns. The property values I need are still as key:pair values in rows and I would like them as columns.
So the output for the above would be:
id 3095 3096 3097
1 5 9 8
whereas currently it is returning from the above query:
id dataid data dataid_1 data_1 dataid_2 data_2
1 3095 5 Unnecessary stuff because of JOIN
1 3096 9
1 3097 8
Thanks and sorry if this is confusing.
SELECT id
FROM (SELECT DISTINCT id
FROM table) as ids
WHERE EXISTS (SELECT '1'
FROM table t2
WHERE t2.id = ids.id AND dataid = 3095 AND data = 5)
AND EXISTS (SELECT '1'
FROM table t2
WHERE t2.id = ids.id AND dataid = 3096 AND data = 9)
Also, if you're querying additional tables besides the given one (one with id as a unique key, preferrably), consider including that, to remove the need to use DISTINCT

T-SQL Recursion; Multiple Recursions?

I'm trying to conceptualize a solution to a problem I have with recursion and I can't quite wrap my mind around it. I have three tables. We'll call them DocGroup, GroupGroup, and GroupUser. In the DocGroup table there is a hirerachial structure of one record, which specifies another record as it's parent and so on until a record is its own parent.
Doc Group
1 1
2 2
3 2
4 3
5 2
6 4
GroupGroup contains Group nesting:
Group MemberGroup
4 2
4 1
GroupUser maps a group to a number of users:
Group User Key
1 1 ABC
1 3 BCD
1 4 CDE
2 1 DEF
2 2 EFG
2 3 FGH
3 3 GHI
4 2 HIJ
4 3 IJK
4 4 JKL
So I would like to specify a user and a document and get a list of all the keys for that combination. For example if I choose user 2 and document 2 I need to return "EFG" but if I choose document 6 and user 3 I need to return "IJK", "GHI", "FGH", and "BCD" because group 2 and 1 are in group 4 and group 4 = document 4, which has a group of 3. In all cases I only need to get the record for the specified user.
I would imagine that I need multiple recursions to get this done, but I can't seem to mentally map out how that might be done in SQL and I don't want to degrade to loops and cursors to do this.
OK, here is the answer:
DECLARE #Key varchar(max)
;WITH DocBase AS (
SELECT Doc, Group
FROM DocGroup
WHERE Doc = #Doc
UNION ALL
SELECT DocGroup.Doc, DocGroup.Group
FROM DocGroup INNER JOIN DocBase
ON DocGroup.Doc = DocBase.Group
WHERE DocBase.Doc <> DocBase.Group --to prevent infinite recursion
), GroupNesting AS (
SELECT Doc
FROM DocBase
UNION ALL
SELECT MemberGroup
FROM GroupGroup.Group = GroupNesting.Doc
), GroupList AS (
SELECT DISTINCT Doc
FROM GroupNesting
), KeyList AS (
SELECT Key
FROM GroupList INNER JOIN GroupUser
ON GroupList.Doc = GroupUser.Group
WHERE User = #User
)
SELECT #Key = COALESE(#Key, '') + Key
FROM Key
SELECT #Key
I could have used any of the suggestions from http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/ for doing the final concatenation, but I chose this method because it is the easiest and I'm putting this in a procedure anyway.
I have to post this because I'm a raging egomanic:
The Zen of Recursion
But seriously, that's how we did it back in the day. These days, you'd use a Common Table Expression.
HTH.