count nested tree user - sql

id left_User_ID right_User_ID referral_Id
-- ------------ ------------- -----------
1 2 5 1
2 3 4 1
3 null null 2
I want to cacluate how many member are under user 1.
for this i create a function that count left side only.
so the its goes like 1->2->3.so it count 2.
same thing done for calculating right side.
so what to do for calculating all side means left and right

SELECT
Sum(
CASE WHEN left_user_ID is null THEN 0 ELSE 1 END +
CASE WHEN right_user_ID is null THEN 0 ELSE 1 END
)
FROM
Table
WHERE
referral_Id = 1

this is recursion...and Recursion can be possible only with cte in sql. I used MS SQL server 2005 . i try this for counting left side node
HERE MY TABLE NAME IS LEG
create function left_legs(#id int)
RETURNS varchar(4) AS BEGIN
DECLARE #count int set #count=0 WHILE #id !=0
BEGIN
set#id=cast((SELECT left_user_Id from Leg where id=#id)as varchar(4))
IF #id != 0
BEGIN
set #count =#count +'1'
END
END
RETURN #count
END
This function count total left nodes.only in one direction.

Without a more specific definition of the problem, it's hard to give good help. However, it sounds like you're halfway there, if you can count the left-hand entries.
Rather than counting left-hand entries, try counting both sides. Psuedo-code for this looks something like this:
public static int leafCount(Node node) {
if (node == null) {
return 0;
}
return 1 + leafCount(node.left) + leafCount(node.right);
}
If whatever backend language you're using has a library for a tree structure, I would expect there is already a similar function provided.
EDIT:
After revealing that we do indeed have a tree table structure, queryable in SQL -
Table:
CREATE TABLE RECURSE (ID INT NOT NULL WITH DEFAULT,
R_SIDE INT NOT NULL WITH DEFAULT,
L_SIDE INT NOT NULL WITH DEFAULT)
Data:
INSERT INTO RECURSE VALUES (1, 2, 5)
(2, 3, 4)
(3, 0, 0)
(4, 0, 0)
(5, 0, 0)
Resulting setup:
Id R_Side L_Side
1 2 5
2 3 4
3 0 0
4 0 0
5 0 0
This statement will should return the count of the left and right nodes. Under DB2 at the least, it seems that there are restrictions that make left joins difficult to employ inside recursive CTEs, so you have to count the left and right side separately. Use this as a starting point, if it doesn't exactly meet your needs.
WITH left_side (id, count) as (SELECT id, 0
FROM recurse
WHERE L_Side = 0
UNION ALL
SELECT a.id, 1 + b.count
FROM recurse as a
JOIN left_side as b
ON b.id = a.l_side
where a.l_side > 0),
right_side (id, count) as (SELECT id, 0
FROM recurse
WHERE R_Side = 0
UNION ALL
SELECT a.id, 1 + b.count
FROM recurse as a
JOIN right_side as b
ON b.id = a.R_Side
where a.l_side > 0)
SELECT a.id, COALESCE(b.count, 0) + COALESCE(c.count, 0) as LeafCount
FROM recurse as a
LEFT JOIN right_side as b
ON b.id = a.id
LEFT JOIN left_side as c
ON clid = a.id
Yields:
Id LeafCount
1 3
2 2
3 0
4 0
5 0

Related

Return Parameter If in Child Table Meet Some Condition

I have table like this
Parent table
id, column1, etc
- - -
- - -
Detail
id, parent_id, column1, actual_finish (value is true/false)
- - - -
- - - -
I want to check if all column actual_finish have value true, then return 1 (I think this will be return parameter), else return 0.
For example
parent
id column1 etc,
------------------
1 value1 a
Detail
id, parent_id, column1, actual_finish (value is true/false)
------------------------------------------------------------
1 1 a true
2 1 b false
This will return 0, because second row actual finish value is false, but if second row column actual_finish updated to true, then return 1
I want to create a stored procedure that returns 0 or 1 based on column actual_finish in the detail table.
Can someone help me?
try using,
select min(case when actual_finish='true' then 1 else 0 end) from tab_Detail where parent_id=1;
SP, something like..
create procedure getStatusById
(#id int)
as
begin
declare #res as int=0
set #res=(select min(case when actual_finish='true' then 1 else 0 end) from tab_Detail
where parent_id=#id);
return #res
print(#res)
end
Fiddle
Query you could use is
Select returnvalue= case when totalCount=trueCount then 1 else 0 end
from
(select
trueCount=count (case when actual_finish ='true' then 1 else 0 end),
totalCount = count(1)
from
parent p left join detail d
on p.id=d.parent_id
group by p.id
)T
This is assuming that you return false if there is no row in detail table for parent id
I think requirement is,even one row exists where actual_fnish is false then return 0.
declare #res as int=1
If exists(select 1 from dbo.tbldetail where actual_finish='false')
set #res=0
actual_finish should be BIT data type (0 or 1)
I would recommend using exists. To handle multiple rows in parents:
select p.*,
(case when exists (select 1
from details d
where d.parent_id = p.id and
d.actual_finish = 'false'
)
then 0 else 1
end) as flag
from parents p;

How to provide dynamic usage of select query in SQL Server 2008 based on a variable value

I have a table myTable with these columns and sample data:
Id Name Period0Status Period1Status Period2Status
-------------------------------------------------------
1 Mark 1 2 3
2 John 2 3 3
3 Brad 1 1 1
4 John 3 3 3
5 Mark 1 3 2
etc...
What I want is to use those data but the column names should be taken according to a SQL variable. What I need is something like that:
declare #0_period varchar(50) = 'Period_0'
declare #1_period varchar(50) = 'Period_1'
declare #2_period varchar(50) = 'Period_2'
declare #counter = 0;
while #counter < 3
begin
insert into #Results(Period, JohnVolume, MarkVolume, JoshVolume)
select
'#' + #counter + '_period',
sum(case when(name = 'John' and (Period+#counter+Status = 3) then 1 else 0)),
sum(case when(name = 'Mark' and (Period+#counter+Status = 3) then 1 else 0)),
sum(case when(name = 'Brad' and (Period+#counter+Status = 3) then 1 else 0))
from
myTable
set #counter = #counter + 1
end
I could not find the correct syntax to provide the dynamic query here. Any help would be appreciated. SQL Server 2008 is used.
Output table I want to produce be like:
Period JohnVolume MarkVolume BradVolume
Period_0 1 0 0
Period_1 2 1 0
Period_2 2 1 0
It should basically counts the amount of 3s for each (John,Mark and Brad) for each period. I gotta find out how to correct the syntax for the parts that #counter is used inside the select query.
Here's an example of how you could do this, with a cross apply to derive the values for each period and a count(case...) for the end values:
SELECT period
, COUNT(CASE WHEN Name = 'John' AND val = 3 THEN 1 END) [JohnVolume]
, COUNT(CASE WHEN Name = 'Mark' AND val = 3 THEN 1 END) [MarkVolume]
, COUNT(CASE WHEN Name = 'Brad' AND val = 3 THEN 1 END) [BradVolume]
FROM myTable
CROSS APPLY (VALUES ('Period_0', Period0Status), ('Period_1', Period1Status), ('Period_2', Period2Status)) unp(period, val)
GROUP BY period
The cross apply works as an unpivot to get each period status and the values within them, and the conditional case statements work as a pivot.
Alternatively, you could use an actual UNPIVOT/PIVOT if you want. An example would be:
SELECT Period, John JohnVolume, Mark MarkVolume, Brad BradVolume
FROM (
SELECT *
FROM myTable
UNPIVOT (val FOR period IN (Period0Status, Period1Status, Period2Status)) U
WHERE val = 3) T
PIVOT (COUNT(val) FOR Name IN ([John], [Mark], [Brad])) P

Union different tables with differents columns and data

My problem is that I have 4 differents SELECT with
SELECT COUNT (*) AS regular
WHERE experience = 1 AND bl = 1
SELECT COUNT (*) AS rptmm
WHERE experience = 1 AND bl = 0
SELECT COUNT (*) AS new
WHERE experience = 0 AND bl = 0
SELECT COUNT (*) AS rptss
WHERE experience = 0 AND bl = 1
I want that the results appear together whith the respective names like:
regular rptmm new rptss
10 5 2 6
Firstly, I'd suggest not to use Count()*. There are many answers on this site explaining why so I am not going to repeat it.
Instead, I'd suggest you to use a query like this:
SELECT (SELECT COUNT (tab.someColumnName)
FROM TableName tab
WHERE tab.experience = 1 AND tab.bl = 1) AS 'Regular',
(SELECT COUNT (tab.someColumnName)
FROM TableName tab
WHERE tab.experience = 1 AND tab.bl = 0) AS 'rptmm',
(SELECT COUNT (tab.someColumnName)
FROM TableName tab
WHERE tab.experience = 0 AND tab.bl = 0) AS 'New',
(SELECT COUNT (tab.someColumnName)
FROM TableName tab
WHERE tab.experience = 0 AND tab.bl = 1) AS 'rptss'
Hope this helps!!!
Just put UNION ALL between your four statements you will get four rows with each count on its own row. However, you will lose the column name. You could also use join to get one row with four columnes. Just put the keyword join between each sql statement.
SELECT COUNT (*) AS regular
WHERE experience = 1 AND bl = 1
JOIN
SELECT COUNT (*) AS rptmm
WHERE experience = 1 AND bl = 0
JOIN
SELECT COUNT (*) AS new
WHERE experience = 0 AND bl = 0
JOIN
SELECT COUNT (*) AS rptss
WHERE experience = 0 AND bl = 1
You could create a temp table to hold all of this data for you: Replace Name1, Name2, Name3,Name4 with whatever you want to call them. These will be the column headers.
CREATE TABLE #Temp(
NAME1 INT
,NAME2 INT
,NAME3 INT
,NAME4 INT
)
INSERT INTO #Temp
(NAME1)
SELECT COUNT(*) AS regular
WHERE experience = 1 AND bl = 1
INSERT INTO #Temp
(NAME2)
SELECT COUNT(*) AS regular
WHERE experience = 1 AND bl = 0
INSERT INTO #Temp
(NAME3)
SELECT COUNT(*) AS regular
WHERE experience = 0 AND bl = 0
INSERT INTO #Temp
(NAME4)
SELECT COUNT(*) AS regular
WHERE experience = 0 AND bl = 1*
SELECT * FROM #Temp

Counting if data exists in a row

Hey guys I have the below sample data which i want to query for.
MemberID AGEQ1 AGEQ2 AGEQ2
-----------------------------------------------------------------
1217 2 null null
58458 3 2 null
58459 null null null
58457 null 5 null
299576 6 5 7
What i need to do is to lookup the table and if any AGEx COLUMN contains any data then it counts the number of times there is data for that row in each column
Results example:
for memberID 1217 the count would be 1
for memberID 58458 the count would be 2
for memberID 58459 the count would be 0 or null
for memberID 58457 the count would be 1
for memberID 299576 the count would be 3
This is how it should look like in SQL if i query the entire table
1 Children - 2
2 Children - 1
3 Children - 1
0 Children - 1
So far i have been doing it using the following query which isnt very efficient and does give incorrect tallies as there are multiple combinations that people can answer the AGE question. Also i have to write multiple queries and change the is null to is not null depending on how many children i am looking to count a person has
select COUNT (*) as '1 Children' from Member
where AGEQ1 is not null
and AGEQ2 is null
and AGEQ3 is null
The above query only gives me an answer of 1 but i want to be able to count the other columns for data as well
Hope this is nice and clear and thank you in advance
If all of the columns are integers, you can take advantage of integer math - dividing the column by itself will yield 1, unless the value is NULL, in which case COALESCE can convert the resulting NULL to 0.
SELECT
MemberID,
COALESCE(AGEQ1 / AGEQ1, 0)
+ COALESCE(AGEQ2 / AGEQ2, 0)
+ COALESCE(AGEQ3 / AGEQ3, 0)
+ COALESCE(AGEQ4 / AGEQ4, 0)
+ COALESCE(AGEQ5 / AGEQ5, 0)
+ COALESCE(AGEQ6 / AGEQ6, 0)
FROM dbo.table_name;
To get the number of people with each count of children, then:
;WITH y(y) AS
(
SELECT TOP (7) rn = ROW_NUMBER() OVER
(ORDER BY [object_id]) - 1 FROM sys.objects
),
x AS
(
SELECT
MemberID,
x = COALESCE(AGEQ1 / AGEQ1, 0)
+ COALESCE(AGEQ2 / AGEQ2, 0)
+ COALESCE(AGEQ3 / AGEQ3, 0)
+ COALESCE(AGEQ4 / AGEQ4, 0)
+ COALESCE(AGEQ5 / AGEQ5, 0)
+ COALESCE(AGEQ6 / AGEQ6, 0)
FROM dbo.table_name
)
SELECT
NumberOfChildren = y.y,
NumberOfPeopleWithThatMany = COUNT(x.x)
FROM y LEFT OUTER JOIN x ON y.y = x.x
GROUP BY y.y ORDER BY y.y;
I'd look at using UNPIVOT. That will make your wide column into rows. Since you don't care about what value was in a column, just the presence/absence of value, this will generate a row per not-null column.
The trick then becomes mashing that into the desired output format. It could probably have been done cleaner but I'm a fan of "showing my work" so that others can conform it to their needs.
SQLFiddle
-- Using the above logic
WITH HadAges AS
(
-- Find everyone and determine number of rows
SELECT
UP.MemberID
, count(1) AS rc
FROM
dbo.Member AS M
UNPIVOT
(
ColumnValue for ColumnName in (AGEQ1, AGEQ2, AGEQ3)
) AS UP
GROUP BY
UP.MemberID
)
, NoAge AS
(
-- Account for those that didn't show up
SELECT M.MemberID
FROM
dbo.Member AS M
EXCEPT
SELECT
H.MemberID
FROM
HadAges AS H
)
, NUMBERS AS
(
-- Allowable range is 1-6
SELECT TOP 6
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS TheCount
FROM
sys.all_columns AS SC
)
, COMBINATION AS
(
-- Link those with rows to their count
SELECT
N.TheCount AS ChildCount
, H.MemberID
FROM
NUMBERS AS N
LEFT OUTER JOIN
HadAges AS H
ON H.rc = N.TheCount
UNION ALL
-- Deal with the unlinked
SELECT
0
, NA.MemberID
FROM
NoAge AS NA
)
SELECT
C.ChildCount
, COUNT(C.MemberID) AS Instances
FROM
COMBINATION AS C
GROUP BY
C.ChildCount;
Try this:
select id, a+b+c+d+e+f
from ( select id,
case when age1 is null then 0 else 1 end a,
case when age2 is null then 0 else 1 end b,
case when age3 is null then 0 else 1 end c,
case when age4 is null then 0 else 1 end d,
case when age5 is null then 0 else 1 end e,
case when age6 is null then 0 else 1 end f
from ages
) as t
See here in fiddle http://sqlfiddle.com/#!3/88020/1
To get the quantity of persons with childs
select childs, count(*) as ct
from (
select id, a+b+c+d+e+f childs
from
(
select
id,
case when age1 is null then 0 else 1 end a,
case when age2 is null then 0 else 1 end b,
case when age3 is null then 0 else 1 end c,
case when age4 is null then 0 else 1 end d,
case when age5 is null then 0 else 1 end e,
case when age6 is null then 0 else 1 end f
from ages ) as t
) ct
group by childs
order by 1
See it here at fiddle http://sqlfiddle.com/#!3/88020/24

Select query to return ids based on values

I need a select statement that will parse an xml and return the Ids based on a condition. how do i use a loop in this case?
I have fruitIds passed in the xml. Each of the fruitids are checked in the fruitstable if it exists also if either it is fresh-0 or not-1. If sold out-(2) then it should not be taken into consideration. The SP must return distinct ids of the fruit that have IsFresh set to 0 or 1
CREATE PROCEDURE [dbo].[Fruits]
#Data XML
AS
BEGIN TRY
SET #Data = '<Fruits>
<Fruit FruitID="1"></Fruit>
<Fruit FruitID="2"></Fruit>
</Fruits>'
SELECT DISTINCT(FruitType)
from dbo.FruitsTable
where FruitID = (SELECT Fruit.cols.value('#FruitID', 'INT') FruitID
FROM #Data.nodes('/Fruits/Fruit') Fruit(cols))
AND (Fruit.IsFresh = 0 OR Fruit.IsFresh = 1)
END TRY
BEGIN CATCH
END CATCH
GO
FruitsTable
Composite Key = fruitid,buyerid
FruitID IsFresh BuyerID(this if FK)
1 0 1
2 1 2
3 0 2
4 0 3
5 2 1
1 1 2
Senthil, You are very close, meaning your logic is sound, just your syntax is off. You need to use IN instead of "=" (IN checks if the value is in a set) and fix your table aliasing:
SELECT DISTINCT f.FruitId
from dbo.FruitsTable f
where f.FruitID in (
SELECT Fruit.cols.value('#FruitID', 'INT') FruitID
FROM #Data.nodes('/Fruits/Fruit') Fruit(cols))
AND (f.IsFresh in (0,1))