SQL Server - build up query using table valued function - sql-server

I have the following tables structure:
tblMapping
map_id | name | parent_id
1 A 0
2 B 1
3 C 2
4 D 2
5 E 4
tblEditableContent
id | map_id | desc_id | isExcluded
1 1 0 0
2 4 0 1
In this table, tblEditableContent, desc_id=0 means that all elements below the given map_id (all its children) are allowed to be displayed and desc_id=1 means that the node itself is selected.
I also have a table valued function (mapping) that will build up the node for a given map_id, similar to the result below:
for select * from dbo.mapping(1)
map_id | name
1 A
2 B
3 C
4 D
5 E
for select * from dbo.mapping(4)
map_id | name
4 D
5 E
Now, for the task and requirement.
I try to create the following result:
map_id | name | desc_id | isExcluded
1 A 0 0
2 B 0 0
3 C 0 0
In the same time, if we change the value of desc_id in tblEditableContent from 0 to 1, then the output should be:
map_id | name | desc_id | isExcluded
1 A 0 0
2 B 0 0
3 C 0 0
5 E 0 0
And here is what I have tried out until now:
select ca.map_id, ca.name, ec.desc_id, ec.isExcluded from tblEditableContent ec
CROSS APPLY (
Select * From dbo.mapping(ec.map_id)
) ca
where ec.isExcluded = 0
order by ca.map_id

You can try a query like below:
select
ec.*
from tblEditableContent ec
join
(
select
ca.map_id as map_id,
MAX(ec.isExcluded) as isExcluded
from tblEditableContent ec
OUTER APPLY
(
Select map_id from dbo.mapping(ec.map_id) where ec.desc_id =0 or (ec.desc_id =1 and map_id=ec.map_id)
) ca
group by ca.map_id
)ca
on ca.isExcluded=0 and ec.map_id=ca.map_id

Related

SQL child parent hierarchy - Using a where on child to hide parent

Let's say I have table below:
ID | Name | Active | ParentID
1 | Foo1 | 1 | 0
2 | Foo2 | 1 | 1
3 | Foo3 | 1 | 2
4 | Foo4 | 1 | 3
5 | Foo5 | 1 | 3
6 | Foo6 | 0 | 5
7 | Foo7 | 1 | 2
7 | Foo7 | 1 | 6
8 | Foo8 | 1 | 7
9 | Foo9 | 1 | 5
(I have indeed duplicate ID's, on which I expressed my thoughts but to no result)
As you can see, once child can have multiple parents. ID's with ParentID 0 have no parent. I need to select all ID's that are active and do not have an inactive parent above them, however high in the tree that might be.
So with the data set above, my result would be:
ID | Name |
1 | Foo1 |
2 | Foo2 |
3 | Foo3 |
4 | Foo4 |
5 | Foo5 |
9 | Foo9 |
ID 6 got removed because it was Inactive
ID 7 got removed because one of its parents (6) is inactive
ID 8 got removed because a parent (6) of its parent (7) is inactive
ID 9 is fine because its parent (5) is active and so are 5 his parents etc
I attempted this with a subquery in the where
SELECT *
FROM table
WHERE ID not in (SELECT ID FROM table where Active = 0)
But that only solves it for the current record.
I've also tried a typical self-join as used for employee/manager, but that only goes one layer deep, while here I also need to check for the parent of the parent etc
Any suggestions/ideas?
One method would be to use an rCTE to work through the hierachy, with a column that retains the initial ID. Then you can use an EXISTS to ensure there are no rows with a value of 0 for Active:
WITH rCTE AS(
SELECT ID,
Name,
Active,
ParentID,
ID AS InitialID
FROM dbo.YourTable YT
UNION ALL
SELECT YT.ID,
YT.Name,
YT.Active,
YT.ParentID,
r.InitialID
FROM rCTE r
JOIN dbo.YourTable YT ON r.ParentID = YT.ID)
SELECT *
FROM dbo.YourTable YT
WHERE NOT EXISTS (SELECT 1
FROM rCTE r
WHERE r.InitialID = YT.ID
AND r.Active = 0);
I would use a recursive CTE to identify IDs where the chain is continuous, using both conditional and unconditional increment by 1 as follows:
With A As
(Select ID, [Name], Active, ParentID, 0 As NUM_1, 0 As NUM_2
From Tbl Where ParentID=0
Union All
Select Tbl.ID, Tbl.[Name], Tbl.Active, Tbl.ParentID,
NUM_1 + 1 As NUM_1,
NUM_2 + IIF(Tbl.Active=1,1,0) As NUM_2
From Tbl Inner Join A On (Tbl.ParentID=A.ID)
)
Select ID, [Name]
From A
Where ID Not In (Select ID From A Where NUM_1<>NUM_2)
Order by ID
Result:
ID
Name
1
Foo1
2
Foo2
3
Foo3
4
Foo4
5
Foo5
9
Foo9
db<>fiddle

Finding Records with matching records in another table

I have 2 tables
Table 1
A | B | C | D | E | F
a Mi 2 1 4 001
b Ma 3 1 4 001
c NA 1 1 4 001
b Na 3 1 4 001
d Na 2 1 4 001
a Mi 2 1 4 002
b Na 3 1 4 002
c Ma 1 1 4 002
d Na 2 1 4 001
Table 2
A | B | C | D | E
a Mi 2 1 4
b Ma 3 1 4
c NA 1 1 4
d Na 2 1 4
OutPut :
F | D
001 1
So columns A, B, C, D, E and F are all columns that specific conditions in them. Table 1 is the table with data that needs to be compare to data in table2. If all records in different columns except F match from Table1 to the records in Table2, only those records should be selected in the output.
Only 001 from column F is displayed because it has all the 4 rows with the same values in the same columns as given in Table 2. Records with value 002 in column F are not selected because they do not have all the rows in table 2. They do have all 4 rows but the record with b does not have all the same matching values.
The final result need not be the output i have mentioned. It could just be all those rows that match the rows given in Table 2. The output is just what the last step is . I can achieve that if i get all rows that match all the records in table 2 like by like.
Something I tried-
select count(A) over(Partition by A,B,C,D,E,F) as rw,*
into #temp1
from Table1
select sum(rw) as sm, F
from #temp1 group by F
select F
from #temp
where sm = (select count(A) from Table2)
One of the issues with this logic is that 002 can have 2-3 duplicated rows which might result in the count being equal to the count of rows in table2 .
With a join of the tables and then group by F:
select t1.f, max(t1.d) d
from table2 t2 inner join (select distinct * from table1) t1
on t1.A = t2.A and t1.B = t2.B and t1.C = t2.C and t1.D = t2.D and t1.E = t2.E
group by t1.f
having count(*) = (select count(*) from table2)
I used max(t1.d) as it is not clear if the value of D is the same for each F.
See the demo.
Results:
> f | d
> :-- | -:
> 001 | 1
If you want the rows from table1 that match the rows from table2, use a CTE:
with cte as (
select t1.f
from table2 t2 inner join (select distinct * from table1) t1
on t1.A = t2.A and t1.B = t2.B and t1.C = t2.C and t1.D = t2.D and t1.E = t2.E
group by t1.f
having count(*) = (select count(*) from table2)
)
select t1.* from table1 t1
where
t1.f in (select f from cte)
and exists (
select 1 from table2 t2
where t1.A = t2.A and t1.B = t2.B and t1.C = t2.C and t1.D = t2.D and t1.E = t2.E
)
See the demo.
Results:
> A | B | C | D | E | F
> :- | :- | -: | -: | -: | :--
> a | Mi | 2 | 1 | 4 | 001
> b | Ma | 3 | 1 | 4 | 001
> c | NA | 1 | 1 | 4 | 001
> d | Na | 2 | 1 | 4 | 001
> d | Na | 2 | 1 | 4 | 001
If you want distinct rows use:
select distinct t1.* from table1 t1
instead.
Results:
> A | B | C | D | E | F
> :- | :- | -: | -: | -: | :--
> a | Mi | 2 | 1 | 4 | 001
> b | Ma | 3 | 1 | 4 | 001
> c | NA | 1 | 1 | 4 | 001
> d | Na | 2 | 1 | 4 | 001
Disregarding the suspect row I mention in the comment, I think this is what you want:
select *
from [Table 1] t1
where exists
(
select 1
from [Table 2] t2
where
t1.A=t2.A
and t1.B=t2.B
and t1.C=t2.C
and t1.D=t2.D
and t1.E=t2.E
)

Sql rows count with grouping

Hello I have two tables as below
tblContactType
typeId typeName active
1 Email 1
2 Phone 1
3 Address 1
4 Fax 1
tblContact
id IdName typeId groupId
100 test 1 1
101 test2 1 1
102 test3 1 2
103 test4 2 2
104 test5 2 3
105 test6 3 3
Want the results to be with column names as typeName count and grouped by group id. Results should be total number of types associated to a group,which are associated to a contact.
GroupId EmailCount PhoneCount AddressCount FaxCount
1 2 0 0 0
2 1 1 0 0
3 0 1 1 0
You can group by and pivot as below:
Select * from (
Select t.groupid, tct.typename, t.id from tblContact t
inner join tblContactType tct
on t.typeid = tct.typeid
) a
pivot (count(a.id) for typename in ([Email],[Phone],[Address],[Fax]) ) p
For dynamic list of columns you can use dynamic query as below:
declare #cols1 varchar(max)
declare #query nvarchar(max)
Select #cols1 = stuff((Select distinct ','+QuoteName(typename) from tblContactType for xml path('')),1,1,'')
Set #query = ' Select * from (
Select t.groupid, tct.typename, t.id from tblContact t
inner join tblContactType tct
on t.typeid = tct.typeid
) a
pivot (count(a.id) for typename in (' + #cols1 + ') ) p '
Select #query --Check the generated query is good and then execute below
--exec sp_executesql #query
Output as below:
+---------+---------+-------+-----+-------+
| groupid | Address | Email | Fax | Phone |
+---------+---------+-------+-----+-------+
| 1 | 0 | 2 | 0 | 0 |
| 2 | 0 | 1 | 0 | 1 |
| 3 | 1 | 0 | 0 | 1 |
+---------+---------+-------+-----+-------+
Here is another solution.
SELECT groupId,
SUM(CASE WHEN c.typeId = 1 THEN 1 ELSE 0 END) 'EmailCount',
SUM(CASE WHEN c.typeId = 2 THEN 1 ELSE 0 END) 'PhoneCount',
SUM(CASE WHEN c.typeId = 3 THEN 1 ELSE 0 END) 'AddressCount',
SUM(CASE WHEN c.typeId = 4 THEN 1 ELSE 0 END) 'FaxCount'
FROM tblContact c
JOIN tblContactType ct ON c.typeId = ct.typeId
GROUP BY groupId
Results
-------------------------------------------------------------
groupId | EmailCount | PhoneCount | AddressCount | FaxCount
-------------------------------------------------------------
1 | 2 | 0 | 0 | 0
2 | 1 | 1 | 0 | 0
3 | 0 | 1 | 1 | 0
-------------------------------------------------------------

How to transpose rows to columns in SQL Server

I have a fact table as below:
Table Types
TypeId | Name
1 x
2 y
3 z
Table Period:
PeriodId | Date
1 2014-01-31
2 2015-01-31
RowNumber | Value | TypeId | Identifier | PeriodId
1 12 1 cc1 1
2 10 2 cc1 2
3 17 3 cc1 1
.. 30 ... ... ..
.. 60 1 cc2 1
23 2 cc2 2
From these tables I am trying to create a single flatten table as below:
Identifier | periodId | x | y | z
cc1 1 12 10 17
cc1 2 .. .. ..
cc2 1 .. .. ..
How can I query to get the data in the above format?
INSERT INTO NewTable
SELECT Identifier, PeriodId,
(SELECT Value FROM [Values] v2 WHERE v2.Identifier = v1.Identifier AND v2.PeriodId = v1.PeriodId AND v2.TypeId = 1) AS 'x',
(SELECT Value FROM [Values] v2 WHERE v2.Identifier = v1.Identifier AND v2.PeriodId = v1.PeriodId AND v2.TypeId = 2) AS 'y',
(SELECT Value FROM [Values] v2 WHERE v2.Identifier = v1.Identifier AND v2.PeriodId = v1.PeriodId AND v2.TypeId = 3) AS 'z'
FROM [Values] v1
GROUP BY Identifier, PeriodId
Is this what you need?

SQL Server query to retrieve all from View

I have a SQL View, similiar to the one below:
map_id | type_id | path
1 1 0
2 2 0
3 3 0
4 1 A>B
5 1 A>B>C
6 2 T>Z
7 2 T>Z>X
8 3 U
9 3 X>Y
10 1 D
And another table, tblRoles
role_group_id | type_id | map_id
1 1 1
2 1 4
I want to build a query that will include all map_id where role_group_id has the map_id = 1 and for the rest it should only get the corresponding map_id
So, the query result should look like:
role_group_id | type_id | map_id | path
1 1 4 A>B
1 1 5 A>B>C
1 1 10 D
1 1 1 0
2 1 4 A>B
Could someone point me to the correct way?
Thank you!
Haven't tested this yet, I hope this should work.
SELECT r.role_group_id
,r.type_id
,sv.map_id
,sv.path
FROM tblRoles r
LEFT JOIN sampleView sv ON r.type_id = sv.type_id
WHERE r.map_id = 1
UNION
SELECT r.role_group_id
,r.type_id
,r.map_id
,sv.path
FROM tblRoles r
INNER JOIN sampleView sv ON r.map_id = sv.map_id
WHERE r.map_id <> 1
You can use UNION
#rosuandreimihai: select all when map_id=1 and if map_id is different, select only the
corresponding row
DECLARE #CurrentMapId INT = 1
SELECT * FROM FirstTable
WHERE
TYPE_ID IN
(
SELECT r.type_id FROM tblRoles r
WHERE
map_id = #CurrentMapId
)
UNION
SELECT * FROM FirstTable
WHERE
map_id IN
(
SELECT r.map_id FROM tblRoles r
WHERE
#CurrentMapId = 1 OR
map_id = #CurrentMapId
)

Resources