I am having a table : TblMemberPosition
Running a sql code -
with cte(MemberID, SponserID, Level)
as
(
select MemberID, SponserID, 1 as Level
from TblMemberPosition
where MemberID = 10021
union all
select a.MemberID, a.SponserID , Level + 1
from TblMemberPosition as a
inner join cte as b
on b.MemberID = a.SponserID
)
select (count (MemberID)) as MemberCount, Level
From cte
where Level <= 8
Group by Level
order by Level
Result of above query:
MemberCount Level
1 1
1 2
3 3
1 4
2 5
1 6
1 7
2 8
But I want output Like -
MemberID Level1 Level 2 Level 3 Level 4 .... Level upto 9
10021 1 1 3 1
This code is generating for single MemberId. Can you help to generate this structure for all members.
with cte(MemberID, SponserID, Level)
as
(
select MemberID, SponserID,1 as Level from TblMemberPosition
where MemberID = 10021
union all
select a.MemberID,a.SponserID,Level + 1 from TblMemberPosition
as a inner join cte as b on b.MemberID = a.SponserID
),
ctePivotSource as (
select
(count (MemberID)) as MemberCount,
'Level ' + convert(varchar(1),[Level]) as [Level]
from cte
where Level <=8
group by level
)
select *
from
( select [MemberID],[MemberCount],[Level] from ctePivotSource) src
pivot
(
sum(src.MemberCount) FOR [Level] in ([Level 1],[Level 2],[Level 3],[Level 4],[Level 5],[Level 6],[Level 7],[Level 8])
) as pvt
You original query restricts the columns to 8 (not 9 as in the required result) so I have also limited the pivot to 8 columns.
This should give you want you want for root level members, meaning members who do not have a sponsor.
;WITH cte AS
(
SELECT MemberID,
SponserID,
MemberID AS [Root] -- this will be your grouping
0 AS [Level] -- not sure you want to count yourself
FROM TblMemberPosition
WHERE SponsorID IS NULL -- get highest level members only?
UNION ALL
SELECT a.MemberID,
a.SponserID,
[Root],
[Level] + 1
FROM TblMemberPosition AS a
INNER JOIN cte AS b ON b.MemberID = a.SponserID
)
SELECT [Root] AS MemberID,
COUNT(CASE WHEN [Level] = 1 THEN 1 END) AS [Level1],
COUNT(CASE WHEN [Level] = 2 THEN 1 END) AS [Level2],
COUNT(CASE WHEN [Level] = 3 THEN 1 END) AS [Level3],
COUNT(CASE WHEN [Level] = 4 THEN 1 END) AS [Level4],
COUNT(CASE WHEN [Level] = 5 THEN 1 END) AS [Level5],
COUNT(CASE WHEN [Level] = 6 THEN 1 END) AS [Level6],
COUNT(CASE WHEN [Level] = 7 THEN 1 END) AS [Level7],
COUNT(CASE WHEN [Level] = 8 THEN 1 END) AS [Level8],
COUNT(CASE WHEN [Level] = 9 THEN 1 END) AS [Level9]
FROM cte
GROUP BY [Root]
Related
I know just the basics of SQL, and recently at work I stumbled across this situation. It is kinda difficult for me to visualize the problem but I will try my best to explain it.
I have two tables:
Table A:
IDA SomeInfo
------- ----------
1 info1
2 info2
3 info3
4 info4
Table B:
IDB IDA VarName VarValue
------- ------- ------- ---------
1 1 Depth 2
2 1 Length 10
3 2 Depth2 3
4 2 Length 12
5 2 Height 35
6 3 Depth2 2
7 3 Length 11
8 3 Height 38
9 4 Depth 4
10 4 Length 16
And I need to join them in order to get the following result:
Depth
IDA SomeInfo Depth2 Length Height
------- -------- ------- ------- ---------
1 info1 2 10 0
2 info2 3 12 35
3 info3 2 11 38
4 info4 4 16 0
As you can see, I need to put two different VarNames from B's rows into one column, and the other VarNames as different columns. Each with their respective VarValue and IDA, and then when an IDA does not have some VarVal, it need to be 0. I had the following code in my c# string query and worked just fine when I only had to deal with "Depth" and "Depth2":
SELECT
A.IDA, A.SomeInfo,
CASE
WHEN (B.VarValue IS NULL) THEN 0
WHEN B.VarName = 'Depth' OR B.VarName = 'Depth2' THEN B.VarValue
END AS Depth
FROM A LEFT JOIN
(
SELECT B.IDA, B.VarName, B.VarValue FROM B
WHERE (B.VarName = 'Depth' OR B.VarName = 'Depth2')
) B ON (A.IDA = B.IDA)
Then I needed to add more VarNames, so I changed the code as follows:
SELECT DISTINCT
A.IDA, A.SomeInfo,
CASE
WHEN (B.VarName = 'Depth' OR B.VarName = 'Depth2') AND B.VarValue IS NULL THEN 0
WHEN B.VarName = 'Depth' OR B.VarName = 'Depth2' THEN B.VarValue
END AS Depth,
CASE
WHEN B.VarName = 'Length' AND B.VarValue IS NULL THEN 0
WHEN B.VarName = 'Length' THEN B.VarValue
END AS Length
CASE
WHEN B.VarName = 'Height' AND B.VarValue IS NULL THEN 0
WHEN B.VarName = 'Height' THEN B.VarValue
END AS Height
FROM A LEFT JOIN
(
SELECT B.IDA, B.VarName, B.VarValue FROM B
WHERE (B.VarName = 'Depth' OR B.VarName = 'Depth2' OR B.VarName = 'Length' OR
B.VarName = 'Height')
) B ON (A.IDA = B.IDA)
But now I get one row for each VarName. What should I do to get this done?
EDIT: Both answers in this question work for the example I gave, but the one marked is the one that worked the best for me. I had an issue related to DB2 and how the data is structured in an as400 but I figured it out and now everything works. Thanks to T.S. and Gordon Linoff for their answers. They helped me a lot.
You can use conditional aggregation:
select a.ida, a.someinfo,
sum(case when varname in ('Depth', 'Depth2') then varvalue end) as depth,
sum(case when varname in ('Length') then varvalue end) as length,
sum(case when varname in ('Height') then varvalue end) as depth
from tablea a left join
tableb b
on a.ida = b.ida
group by a.ida, a.someinfo;
Here is a db<>fiddle.
In pseudo code - use inline view
select a.IDA, a.SomeInfo, dep.depth, hei.Height, le.Length
from
TableA a left join
(select IDA, VarValue as depth from tableB where varName = 'depth' ) dep
on a.ida = dep.ida letf join
(select IDA, VarValue as height from tableB where varName = 'height' ) hei
on a.ida = hei.ida letf join
(select IDA, VarValue as Length from tableB where varName = 'Length' ) le
on a.ida = le.ida
No doubt, there are other ways. I am old-style
I am not sure what is the DB-structural setup. For example, I don't know id the same IDA can have both, depth and depth2. If only one of those values is possible then you can change filter like
select IDA, VarValue as depth from tableB where varName in ('depth', 'depth2')
But if both is possible, we need another join, and COALESCE in select.
Select ...., COALESCE(dep.depth, dep2.depth2) as depth
....
(select IDA, VarValue as depth from tableB where varName = 'depth' ) dep
on a.ida = dep.ida letf join
(select IDA, VarValue as depth2 from tableB where varName = 'depth2' ) dep2
on a.ida = dep2.ida letf join ...
I have a table like below
Name Year Bonus
---- ----- ------
Ram 2011 1000
Ram 2011 2000
Shyam 2011 'No Bonus'
Shyam 2012 5000
I want to display the total bonus year wise for each person.I tried below query
SELECT [Year],[Ram],[Shyam] FROM
(SELECT Name, [Year] , Bonus FROM Employee )Tab1
PIVOT
(
SUM(Bonus) FOR Name IN (Ram,Shyam)) AS Tab2
ORDER BY [Tab2].[Year]
My Output Should be like below
Name 2011 2012
---- ------ ------
Ram 3000 0
Shyam 'No Bonus' 5000
But it is not working.
Can anyone help me on this?
If your dbms is sql-server you can try to use SUM condition aggregate function in a CTE
then use CAST with coalesce to make it.
;WITH CTE AS(
SELECT Year,Name,
SUM(CASE WHEN Bonus LIKE '%[0-9]%' THEN CAST(Bonus AS DECIMAL) ELSE 0 END) total,
COUNT(CASE WHEN Bonus = 'No Bonus' THEN 1 END) cnt
FROM T
GROUP BY Year,Name
)
SELECT Name,
coalesce(MAX(CASE WHEN Year = 2011 THEN CAST(total AS VARCHAR(50)) END),'No Bonus') '2011',
coalesce(MAX(CASE WHEN Year = 2012 THEN CAST(total AS VARCHAR(50)) END),'No Bonus') '2012'
FROM CTE
GROUP BY Name
sqlfiddle
If you want to create columns dynamically you can try to use dynamic PIVOT.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
;WITH CTE AS(
SELECT Year,Name,
SUM(CASE WHEN Bonus LIKE '%[0-9]%' THEN CAST(Bonus AS DECIMAL) ELSE 0 END) total,
COUNT(CASE WHEN Bonus = 'No Bonus' THEN 1 END) cnt
FROM T
GROUP BY Year,Name
)
SELECT #cols = STUFF((SELECT distinct ',coalesce(MAX(CASE WHEN cnt > 0 and Year = ' + cast(Year as varchar(5)) + ' THEN ''No Bonus'' WHEN Year = ' + cast(Year as varchar(5)) + ' and cnt = 0 THEN CAST(total AS VARCHAR(50)) END),''0'')' + QUOTENAME(Year)
FROM CTE c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = '
;WITH CTE AS(
SELECT Year,Name,
SUM(CASE WHEN Bonus LIKE ''%[0-9]%'' THEN CAST(Bonus AS DECIMAL) ELSE 0 END) total,
COUNT(CASE WHEN Bonus = ''No Bonus'' THEN 1 END) cnt
FROM T
GROUP BY Year,Name
)
SELECT Name, ' + #cols + '
from CTE
GROUP BY Name'
exec(#query)
sqlfiddle
According to your problem the following query is what I understood. Not the ideal solution but this will do.
You can modify the query if you need to make it dynamic.
SELECT [Name]
, case when [2011] = 0 then 'No Bonus' when [2011] is null then '0' else cast([2011] as varchar(50)) end as [2011]
, case when [2012] = 0 then 'No Bonus' when [2012] is null then '0' else cast([2012] as varchar(50)) end as [2012]
FROM
(SELECT Name, [Year] , cast(Bonus as int) Bonus FROM Employee)Tab1
PIVOT
(
SUM(Bonus) FOR Year IN ([2011],[2012])) AS Tab2
ORDER BY [Tab2].[Name]
You need to pass 0 in the table though and then modify in the PIVOT
I would just give up on storing numbers as strings. I don't see the difference between 0/NULL and 'No Bonus', except that the latter makes queries prone to really bad type conversion problems.
So, my advice is to do:
SELECT [Year],[Ram],[Shyam]
FROM (SELECT Name, [Year], TRY_CONVERT(int, Bonus) as Bonus
FROM Employee
) e
PIVOT (SUM(Bonus) FOR Name IN (Ram, Shyam)) AS Tab2
ORDER BY [Tab2].[Year] ;
You probably don't like that solution -- although I really do strongly recommend it because I have spent way too many hours debugging problems with numbers and dates stored as strings.
So, if you persist with storing values as string, use conditional aggregation and a bunch of logic:
select year,
coalesce( sum(case when name = 'Ram'
then convert(varchar(255), try_convert(int, bonus))
end),
'No Bonus'
) as Ram,
coalesce( sum(case when name = 'Shyam'
then convert(varchar(255), try_convert(int, bonus))
end),
'No Bonus'
) as Shyam
from employee e
group by year
order by year;
I have a table with customer transactions that I'm trying to aggregate by customer and department.
Cust_id trans_num sku dept qty price
123 234 345 1 2 15.99
123 345 887 1 1 12.99
123 678 445 2 1 21.89
234 345 998 1 1 7.99
In SQL I'd do something like this:
SELECT Cust_id
, SUM(CASE WHEN dept = 1 THEN (price * qty) ELSE 0 END ) dept_1_spend
, SUM(CASE WHEN dept = 2 THEN (price * qty) ELSE 0 END ) dept_2_spend
from tab1
group by Cust_id
The U-SQL docs here mention ? as the C# equivalent but I'm not sure how to SUM the values.
What's the equivalent in U-SQL?
You can try ternary operator in C#:
SELECT Cust_id
, SUM(dept == 1 ? price * qty : 0) AS dept_1_spend
, SUM(dept == 2 ? price * qty : 0) AS dept_2_spend
from tab1
group by Cust_id
You can also you the U-SQL PIVOT operator, eg
#tab1 =
SELECT *
FROM(
VALUES
(123,234,345,1,2,15.99),
(123,345,887,1,1,12.99),
(123,678,445,2,1,21.89),
(234,345,998,1,1,7.99)) AS T(Cust_id,trans_num,sku,dept,qty,price);
#res =
SELECT Cust_id,
SUM([1]) AS dept_1_spend,
SUM([2]) AS dept_2_spend
FROM
(
SELECT Cust_id, dept, price * qty AS spend
FROM #tab1
) AS t
PIVOT (SUM(spend) FOR dept IN ( 1 AS [1], 2 AS [2] )
) AS pvt
GROUP BY Cust_id;
OUTPUT #res
TO "/output/sum_case.csv"
USING Outputters.Csv();
More information on U-SQL PIVOT available here.
You can even use the SQL's CASE expression. You will need the C# == and use AS to designate the column aliases and use upper-case for the keywords. But otherwise looks like your query:
#tab1 =
SELECT *
FROM(
VALUES
(123,234,345,1,2,15.99),
(123,345,887,1,1,12.99),
(123,678,445,2,1,21.89),
(234,345,998,1,1,7.99)) AS T(Cust_id,trans_num,sku,dept,qty,price);
#res =
SELECT Cust_id,
SUM(CASE WHEN dept == 1 THEN(price * qty) ELSE 0 END) AS dept_1_spend,
SUM(CASE WHEN dept == 2 THEN(price * qty) ELSE 0 END) AS dept_2_spend
FROM #tab1
GROUP BY Cust_id;
OUTPUT #res
TO "/output/sum_case.csv"
USING Outputters.Csv();
I personally prefer the C# ternary if.
How to get the below result from the given data.
Report
-------------------------------------------------
ID | Name | Status
-------------------------------------------------
1 | A | Inprogress
2 | A | Inprogress
3 | A | Complete
I need to calculate the result like
---------------------------------------------
Name | Total | Complete | Remaining
---------------------------------------------
A | 3 | 1 | 2
SQL:
Select
[Inprogress], [Complete], [Printed]
from
(select
specimenID, count([RStatus])
from tbl_LabReport
group by specimenID) as sourcetable
Pivot
(Count(specimenID) for [RStatus] in ( [Inprogress],[Complete],[Printed] )) as ptv
You don't need to use PIVOT operator. Try to use subquery or CROSS APPLY operator to solve your task
;WITH test_data AS(
SELECT 1 AS id, 'A' AS NAME, 'Inprogress' AS [status]
UNION ALL
SELECT 2, 'A', 'Inprogress'
UNION ALL
SELECT 3, 'A', 'Complete'
UNION ALL
SELECT 4, 'B', 'Complete'
)
SELECT DISTINCT
name,
T3.*
FROM test_data AS T
CROSS APPLY (SELECT COUNT(*) AS total,
SUM(CASE WHEN T2.status = 'Complete' THEN 1 ELSE 0 END) AS complete,
SUM(CASE WHEN T2.status = 'Inprogress' THEN 1 ELSE 0 END) AS remaining
FROM test_data AS T2
WHERE T2.name = T.name) AS T3
There are a few approaches you could take. This example uses CASE expressions. If you are not familiar with the concept a CASE allows you to conditional return a selected value. I've converted your sample data into table variable:
Sample Data
/* Creating sample data inside a table variable makes it
* easy to share.
*/
DECLARE #Sample TABLE
(
ID INT,
Name VARCHAR(50),
[Status] VARCHAR(50)
)
;
/* Sample values taken from OP.
*/
INSERT INTO #Sample
(
ID,
Name,
[Status]
)
VALUES
(1, 'A', 'Inprogress'),
(2, 'A', 'Inprogress'),
(3, 'A', 'Complete')
;
The CASE returns a 1 for the required Status and a 0 for everything else. Summing the result provides sub totals.
/* Using CASE to return conditional sub totals.
*/
SELECT
Name,
COUNT([Status]) AS Total,
SUM(CASE WHEN [Status] = 'Complete' THEN 1 ELSE 0 END) AS Complete,
SUM(CASE WHEN [Status] = 'Inprogress' THEN 1 ELSE 0 END) AS Remaining
FROM
#Sample
GROUP BY
Name
;
Suppose I have following data in my table, where an Item has a parent ID 0. But there could only be three sub-levels/children of a parent. That is we need each item that has parentId = 0, and each parent can have up to three levels of children.
pkProductID Name ParentId
-------------------------------------------
1 Cloth 0
2 T-Shirts 1
3 Men-Shirts 2
4 Women-Shirts 2
5 Kids-Shirts 3
6 Cosmetics 0
7 Creams 6
8 Men-cream 7
9 Women-Cream 7
10 Kids-cream 9
We need list that contains items that has parentID=0 and then 2 levels of that children that means from above I need only these items:
1 Cloth
2 T-Shirts
3 Men-Shirts
4 Women-Shirts
6 Cosmetics
7 Creams
8 Men-Cream
9 Women-Cream
The following code should do the work (change table or columns names accordingly):
use [DemoDB]
GO
WITH Emp_CTE AS (
SELECT ProductId, Name, ParentID, 0 as [Level]
FROM [dbo].[Products]
WHERE ParentId = 0
UNION ALL
SELECT p.ProductId, p.Name, p.ParentID, [Level] + 1
FROM [dbo].[Products] as p
INNER JOIN Emp_CTE ecte ON ecte.ProductId = p.ParentID
WHERE [Level] < 2
),
CTE2 AS
(
SELECT [dbo].[Products].*
FROM Emp_CTE
INNER JOIN [dbo].[Products] ON Emp_CTE.ProductId = [dbo].[Products].ParentID
)
SELECT * FROM CTE2
GO
You can use a recursive CTE for this type of task, take a look at this link here that shows a simple example: http://blog.sqlauthority.com/2008/07/28/sql-server-simple-example-of-recursive-cte/ and there's another example here http://msdn.microsoft.com/en-us/library/ms186243%28v=sql.105%29.aspx
CREATE TABLE #clothes(
pkProductID INT,
NAME VARCHAR(50),
ParentId INT
)
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 1, 'cloth', 0 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 2, 'tshirt', 1 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 3, 'mens tshirt', 2 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 4, 'womens tshirt', 2 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 5, 'kids tshirt', 3 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 6, 'cosme', 0 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 7, 'cream', 6 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 8, 'm cream', 7 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 9, 'w cream', 7 )
INSERT INTO #clothes
( pkProductID, NAME, ParentId )
VALUES ( 10, 'kids cream', 9 )
WITH myCTE AS (
SELECT pkProductID ,
NAME ,
ParentId, 0 AS ItemLevel FROM #clothes
WHERE ParentId = 0
UNION all
SELECT c.pkProductID ,
c.NAME ,
c.ParentId, ItemLevel + 1 FROM #clothes c INNER JOIN myCTE mc ON mc.pkProductID = c.ParentId
)
SELECT * FROM myCTE WHERE ItemLevel <=2 ORDER BY pkProductID