I have 2 tables Persons and Sales. In Person there is relation between child and parent, I want to compute 20 percent of parent values with the following condition
Persons
Id | ParentId | Name
1 NULL Tom
2 1 Jake
3 2 Kate
4 3 Neil
Sales
PersonId | Sale
4 500
I want to get result like this
Id | ParentId | Name | Sale
1 Null Tom 100 <-- (500*20)/100 left 400
2 1 Jake 80 <-- (400*20)/100 left 320
3 2 Kate 64 <-- (320*20)/100 left 256
4 3 Neil 256 <-- (320*80)/100
I wrote this query but it does not give appropriate result
;WITH cte_persons
AS
(
SELECT p.Id, p.ParentId, p.Name, s.Price FROM Persons AS p
INNER JOIN Sales AS s ON s.PersonId = p.Id
UNION ALL
SELECT p.Id, p.ParentId, p.Name, CAST((c.Price - (c.Price*80)/100) AS DECIMAL(6, 2)) FROM #Persons AS p
INNER JOIN cte_persons AS c ON c.ParentId = p.Id
)
SELECT * FROM cte_persons
This should be a two steps algorithm. First traverse the hierachy to get max level. Then apply the level in a reverse order.
WITH cte_persons
AS
(
SELECT 1 as level, p.Id, p.ParentId, p.Name, s.Price, p.Id AS base
FROM Persons AS p
INNER JOIN Sales AS s ON s.PersonId = p.Id
UNION ALL
SELECT level + 1, p.Id, p.ParentId, p.Name, c.Price, c.base
FROM Persons AS p
INNER JOIN cte_persons AS c ON c.ParentId = p.Id
)
SELECT Id, ParentId, Name,
CASE level WHEN 1
THEN price - sum(delta) over(partition by base order by level desc) + delta
ELSE delta END sale
FROM (
SELECT *,
(power (0.8000, max(level) over(partition by base) - level) * 0.2) * price delta
FROM cte_persons
) t
ORDER BY id;
db<>fiddle
Related
So I have written a query to get the cumulative sum of children but I think partition sum has error as its totalling for the parent that is not part of the children.
My fiddle is http://sqlfiddle.com/#!15/88828/1
I have dont a running total but thats wrong. I want the siblings total to a child and child total back to its parent.So basically cumulative total of a child up the tree.
expected output
parent_child_tree id name dimensionvalueid level order_sequence volume cummulative_total
A1 1 A1 (null) 0 1 20 840
-----A1:1 2 A1:1 1 1 1_1 (null) 820
----------A1:1:1 3 A1:1:1 2 2 1_1_2 20 820
-----------A1:1:1:1 4 A1:1:1:1 3 3 1_1_2_3 300 800
-----------A1:1:1:2 5 A1:1:1:2. 3 3 1_1_2_3 500 500
B1 6 B1 (null) 0 6 200 300
-----B1:2 8 B1:2 6 1 6_6 (null) null
-----B1:1 7 B1:1 6 1 6_6 (null) 100
----------B1:2:1 9 B1:2:1 8 2 6_6_8 100 100
To get totals for tree nodes you need to generate hierarchy tree for every node in a subquery like this
SELECT
d.*,
v.volume,
(
WITH RECURSIVE cte AS (
SELECT
dd.id AS branch_id,
dd.id
FROM dimensionvalue dd
WHERE dd.id = d.id
UNION ALL
SELECT
cte.branch_id,
dd.id
FROM dimensionvalue dd
JOIN cte ON dd.dimensionvalueid = cte.id
)
SELECT SUM(v.volume)
FROM cte
JOIN valuation v ON v.dimensionvalueid = cte.id
GROUP BY cte.branch_id
) AS totals
FROM dimensionvalue d
LEFT JOIN valuation v ON v.dimensionvalueid = d.id
ORDER BY d.name;
If you really need all those "decoration" columns that you generate in your query for each tree node than you can combine your recursive CTE hierarchy with subquery for totals calculation like this
WITH RECURSIVE hierarchy AS (
SELECT
d.id,
d.name,
d.dimensionvalueid,
0 AS level,
CAST(d.id AS varchar(50)) AS order_sequence
FROM dimensionvalue d
WHERE d.dimensionvalueid IS NULL
UNION ALL
SELECT
e.id,
e.name,
e.dimensionvalueid,
hierarchy.level + 1 AS level,
CAST(hierarchy.order_sequence || '_' || CAST(hierarchy.id AS VARCHAR(50)) AS VARCHAR(50)) AS order_sequence
FROM hierarchy
JOIN dimensionvalue e ON e.dimensionvalueid = hierarchy.id
)
SELECT
RIGHT('-----------', h.level * 5) || h.name || ' ' AS parent_child_tree,
h.*,
v.volume,
(
WITH RECURSIVE cte AS (
SELECT
dd.id AS branch_id,
dd.id
FROM dimensionvalue dd
WHERE dd.id = h.id
UNION ALL
SELECT
cte.branch_id,
dd.id
FROM dimensionvalue dd
JOIN cte ON dd.dimensionvalueid = cte.id
)
SELECT SUM(v.volume)
FROM cte
JOIN valuation v ON v.dimensionvalueid = cte.id
GROUP BY cte.branch_id
) AS totals
FROM hierarchy h
LEFT JOIN valuation v ON v.dimensionvalueid = h.id
ORDER BY h.name
You can check a working demo here
I have two tables defined below (note that the Regions table is recursive and that the recursion can potentially have many levels).
Regions
Id
ParentId
Name
1
null
EU
2
1
Germany
3
1
France
Cities
Id
Name
RegionId
1
Berlin
2
2
Hamburg
2
3
Paris
3
4
Nice
3
I want to see how many cities there are in a particular region. Desired output below:
Region
CityCount
EU
4
Germany
2
France
2
This query gives me the count of cities in every child region, but how do I join in the recursive table to also get the parent (in this case EU) region?
select R.Name, count(C.Id)
from Regions R
join Cities C on C.RegionId = R.Id
group by R.Name
having count(C.Id) > 1
I've tried to simplify a real-world problem I'm facing, this is obviously the simplification.
This appears to be what you're after. You can use an rCTE to move through the hierachy to the root, but each iteration retains certain information; in this case the name of the original node. Then you can still JOIN on the RegionID:
WITH rCTE AS(
SELECT R.ID,
R.ParentID,
R.[Name]
FROM dbo.Regions R
UNION ALL
SELECT R.ID,
R.ParentID,
C.[Name]
FROM dbo.Regions R
JOIN rCTE C ON R.ParentID = C.ID)
SELECT r.[Name],
COUNT(*) AS CityCount
FROM rCTE r
JOIN dbo.Cities C ON r.ID = C.RegionID
GROUP BY r.[Name];
db<>fiddle
You can use a recursive CTE to flatten your regions tree:
with flatregions as
(
select t.ID, t.ParentID, t.Name, 1 as lvl
from regions t
union all
select t.ID, tt.ParentID, tt.Name, t.lvl+1
from flatregions t
inner join regions tt on tt.ID = t.ParentID
)
select * FROM flatregions;
ID
ParentID
Name
lvl
1
EU
1
2
1
Germany
1
3
1
France
1
3
EU
2
2
EU
2
Then use that CTE in the JOIN of your cities table:
with flatregions as
(
select t.ID, t.ParentID, t.Name, 1 as lvl
from regions t
union all
select t.ID, tt.ParentID, tt.Name, t.lvl+1
from flatregions t
inner join regions tt on tt.ID = t.ParentID
)
select R.Name, count(C.Id) as CityCount
from flatregions R
join Cities C on C.RegionId = R.Id
group by R.Name
having count(C.Id) > 1
Region
CityCount
EU
4
Germany
2
France
2
See this db<>fiddle.
I work with Postgresql.
I have a sql code
SELECT lp."RegionId", COUNT(w."Id") FROM public.workplace w
GROUP BY lp."RegionId"
that returns to me
RegionId | Count
1 | 3
2 | 12
3 | 5
I have table 'person'. Each person have RegionId.
So i for region 1 i want to select first 3 persons, for region 2 select first 12 persons, for region 3 select first 5 persons.
So how can i use it as subquery to table 'person'?
WITH (SELECT lp."RegionId", COUNT(w."Id") FROM public.workplace w
GROUP BY lp."RegionId") AS pc
SELECT * FROM public.person p
???????
limit pc."Count"
???
Something like:
SELECT p.*
FROM (SELECT *, row_number() OVER (PARTITION BY RegionId ORDER BY PersonId) AS rn
FROM person) AS p
JOIN (SELECT RegionId, count(*) AS cnt
FROM workplace
GROUP BY RegionId) AS r ON p.RegionId = r.RegionId
WHERE p.rn <= r.cnt
ORDER BY p.RegionId, p.PersonId;
I have tried looking into different topics over here and in other forums but I can't seem to find a solution to my problem.
What I'm trying to achieve is "Display the net sales (in dollars) of the Product Line with the highest revenue for that Customer. Use a heading of: Best Sales. Format as $999,999.99.
Here's what I've tried so far:
SELECT cc.CustID, cc.CompanyName, cc.ContactName, pl.pl_id,to_char((sum(od.unitprice*od.quantity*(1-discount))), '$9,999,999.99') as rev
FROM corp.customers cc JOIN corp.orders co ON (cc.CustID=co.CustID)
LEFT OUTER JOIN corp.order_details od ON (co.orderID=od.orderID)
LEFT OUTER JOIN corp.products cp ON (od.ProductID=cp.ProductID)
LEFT OUTER JOIN corp.product_lines pl ON (cp.pl_id=pl.pl_id)
GROUP BY cc.CustID, cc.CompanyName, cc.ContactName, pl.pl_id
HAVING sum(od.unitprice*od.quantity*(1-discount))=
(
SELECT max(sum(od.unitprice*od.quantity*(1-discount)))
FROM corp.customers cc JOIN corp.orders co ON (cc.CustID=co.CustID)
JOIN corp.order_details od ON (co.orderID=od.orderID)
JOIN corp.products cp ON (od.ProductID=cp.ProductID)
JOIN corp.product_lines pl ON (cp.pl_id=pl.pl_id)
GROUP BY cc.CustID, cc.CompanyName, cc.ContactName, pl.pl_id);
This gives me only one output indicating the highest revenue of all customers, but I would like it to display the highest revenue according to each of the product line for that customer.
The result is shown below.
CustID | Company Name | Contact Name | PL_ID | Revenue
QUICK | QUICK-Stop | Horst Kloss | 1 | $37,161.63
I would like it to show something like.
CustID | Company Name | Contact Name | PL_ID | Revenue
QUICK | QUICK-Stop | Horst Kloss | 1 | $37,161.63
QS | QUICK-Start | Clark Stone | 2 | $50,000.00
QUI | QUICK | Mary Haynes | 1 | $60,000.00
QShelf | QUICK-Shelf | Doreen Lucas | 4 | $35,161.63
Any help is appreciated. Thank you!
This query uses your original query, a rank() function to order by your rev column, and a selection to only get the highest rev. This will give multiple rows if you have multiple rows with the same rev value. Change rank() to row_number() if you only want one.
You could also use CTE instead of the nested queries, wont make any difference.
select CustID, CompanyName, ContactName, pl_id, rev from (
select CustID, CompanyName, ContactName, pl_id, to_char(rev, '$9,999,999.99') as rev,
rank() over(order by rev desc) r
from (
SELECT cc.CustID, cc.CompanyName, cc.ContactName, pl.pl_id,
sum(od.unitprice*od.quantity*(1-discount)) as rev
FROM corp.customers cc JOIN corp.orders co ON (cc.CustID=co.CustID)
LEFT OUTER JOIN corp.order_details od ON (co.orderID=od.orderID)
LEFT OUTER JOIN corp.products cp ON (od.ProductID=cp.ProductID)
LEFT OUTER JOIN corp.product_lines pl ON (cp.pl_id=pl.pl_id)
GROUP BY cc.CustID, cc.CompanyName, cc.ContactName, pl.pl_id
) q
) q2 where r=1
Since you didn't provide us with sample input data for your tables, I've knocked up a simple example that you can hopefully use to amend your query:
WITH sample_data AS (SELECT 1 ID, 1 id2, 10 val FROM dual UNION ALL
SELECT 1 ID, 1 id2, 20 val FROM dual UNION ALL
SELECT 1 ID, 2 id2, 30 val FROM dual UNION ALL
SELECT 1 ID, 2 id2, 40 val FROM dual UNION ALL
SELECT 2 ID, 1 id2, 50 val FROM dual UNION ALL
SELECT 2 ID, 2 id2, 60 val FROM dual UNION ALL
SELECT 2 ID, 3 id2, 60 val FROM dual)
SELECT ID,
id2,
max_sum_val
FROM (SELECT ID,
id2,
SUM(val) sum_val,
MAX(SUM(val)) OVER (PARTITION BY ID) max_sum_val
FROM sample_data
GROUP BY ID, id2)
WHERE sum_val = max_sum_val;
ID ID2 MAX_SUM_VAL
---------- ---------- -----------
1 2 70
2 2 60
2 3 60
This will display all id2 values that have the same sum(val) that's the highest. If you don't want to display all tied rows, you can used the row_number() analytic function instead:
WITH sample_data AS (SELECT 1 ID, 1 id2, 10 val FROM dual UNION ALL
SELECT 1 ID, 1 id2, 20 val FROM dual UNION ALL
SELECT 1 ID, 2 id2, 30 val FROM dual UNION ALL
SELECT 1 ID, 2 id2, 40 val FROM dual UNION ALL
SELECT 2 ID, 1 id2, 50 val FROM dual UNION ALL
SELECT 2 ID, 2 id2, 60 val FROM dual UNION ALL
SELECT 2 ID, 3 id2, 60 val FROM dual)
SELECT ID,
id2,
max_sum_val
FROM (SELECT ID,
id2,
SUM(val) sum_val,
row_number() OVER (PARTITION BY ID ORDER BY SUM(val) DESC, id2) rn
FROM sample_data
GROUP BY ID, id2)
WHERE rn = 1;
ID ID2 MAX_SUM_VAL
---------- ---------- -----------
1 2 70
2 2 60
ETA:
That means your query would end up something like:
SELECT custid,
companyname,
contactname,
pl_id,
to_char(rev, '$9,999,999.99') rev
FROM (SELECT cc.custid,
cc.companyname,
cc.contactname,
pl.pl_id,
SUM(od.unitprice * od.quantity * (1 - discount)) AS rev,
MAX(SUM(od.unitprice * od.quantity * (1 - discount))) OVER (PARTITION BY cc.custid) max_rev
FROM corp.customers cc
INNER JOIN corp.orders co ON (cc.custid = co.custid)
LEFT OUTER JOIN corp.order_details od ON (co.orderid = od.orderid)
LEFT OUTER JOIN corp.products cp ON (od.productid = cp.productid)
LEFT OUTER JOIN corp.product_lines PL ON (cp.pl_id = pl.pl_id)
GROUP BY cc.custid,
cc.companyname,
cc.contactname,
pl.pl_id)
WHERE rev = max_rev;
I need you to help me on writing two queries in SQL Server 2008 that shows the following information based on item master:
Brand wise count on customer master plus customer who purchased the brand
Item Wise count of customer master plus customer who purchased the item
Here the link that shows the table information and the query which I tried.
Click here to view the table in SQL Fiddle
SELECT
brandname,
division,
route,
DivisionTotalCustomersCount = MAX(DivisionTotalCustomersCount),
RouteTotalCustomersCount = MAX(RouteTotalCustomersCount),
PurchasedCustomersCount = SUM(PurchasedCustomersCount)
FROM
(SELECT
i.brandname,
c.division,
c.route,
DivisionTotalCustomersCount =
(SELECT COUNT(distinct x.CustomerID)
FROM CustomerMaster x
WHERE x.division = c.division),
RouteTotalCustomersCount =
(SELECT COUNT(distinct x.CustomerID)
FROM CustomerMaster x
WHERE x.Route = c.route),
PurchasedCustomersCount = count(distinct C.CustomerID)
FROM CustomerMaster c
LEFT OUTER JOIN SalesData s on c.CustomerID = s.CustomerID
right outer join ItemMaster i on s.item = i.itemcode
GROUP BY i.brandname, c.division, c.route) A
GROUP BY
brandname, division, route
ORDER BY 1
Result Should as below
Excelsheet
I think you need to go reconsider the report and maybe splitting it out into multiple reports.
It does not make sense to have a route count as well as a divisional count if they are counting things at different levels of aggregation. So have a route count and division count report.
Either way, division and route is going to be null for 100PLUS because there are no customers for that brand which means there is no route or division info available.
--Division Count
SELECT BrandName, Division, COUNT(CustomerMaster.CustomerID) [Customer Count]
FROM ItemMaster LEFT OUTER JOIN
SalesData ON ItemMaster.BrandName = SalesData.Brand LEFT OUTER JOIN
CustomerMaster ON SalesData.CustomerID = CustomerMaster.CustomerID
GROUP BY BrandName, Division
--Route Count
SELECT BrandName, Route, Division, COUNT(CustomerMaster.CustomerID) [Customer Count]
FROM ItemMaster LEFT OUTER JOIN
SalesData ON ItemMaster.BrandName = SalesData.Brand LEFT OUTER JOIN
CustomerMaster ON SalesData.CustomerID = CustomerMaster.CustomerID
GROUP BY BrandName, Route, Division
Using your sqlfiddle data there are 25 sales records & 18 distinct brand/ division/ route/ customer records and there are no sales invloving 100PLUS
select
B.BrandName
, V.Division
, coalesce(Brand_count,0) as Brand_count
, coalesce(Division_count,0) as Division_count
from (select distinct BrandName from ItemMaster) as B
cross join (select distinct Division from CustomerMaster) as V
left join (
select
Brand
, Division
, sum(cust_count) over (partition by Brand) as Brand_count
, sum(cust_count) over (partition by Division) as Division_count
from (
select
S.Brand
, C.Division
, count(distinct S.CustomerID) cust_count
from salesdata as S
inner join CustomerMaster as C
on S.CustomerID = C.CustomerID
inner join ItemMaster as I
on S.item = I.ItemCode
group by
S.Brand
, C.Division
) as S
) as D
on B.BrandName = D.Brand
and V.Division = D.Division
order by
B.BrandName
, V.Division
;
| BRANDNAME | DIVISION | BRAND_COUNT | DIVISION_COUNT |
|-----------|----------|-------------|----------------|
| 100PLUS | Dubai | 0 | 0 |
| 100PLUS | RAK | 0 | 0 |
| KITCO | Dubai | 9 | 11 |
| KITCO | RAK | 9 | 7 |
| Red Bull | Dubai | 9 | 11 |
| Red Bull | RAK | 9 | 7 |
http://sqlfiddle.com/#!3/fecb0/27
All Credit to #kevriley
select
A.BrandName,
B.Division,
B.Route,
B.DivisionTotalCustomers,
B.RouteTotalCustomers,
isnull(C.PurchasedCustomersCount,0) as PurchasedCustomersCount
from
(
select distinct
BrandName, Route, Division
from dbo.ItemMaster
cross join dbo.CustomerMaster
) A
join
(
select distinct
Division,
Route,
DENSE_RANK() over (partition by Division order by c.CustomerID asc) + DENSE_RANK() over (partition by Division order by c.CustomerID desc) - 1 as DivisionTotalCustomers ,
DENSE_RANK() over (partition by ROUTE order by c.CustomerID asc) + DENSE_RANK() over (partition by ROUTE order by c.CustomerID desc) - 1 as RouteTotalCustomers
from CustomerMaster c
left join SalesData s on c.CustomerID = s.CustomerID
) B on B.Division = A.Division and B.Route = A.Route
left join
(
select
s.brand,
c.division,
c.route,
PurchasedCustomersCount = count(distinct C.CustomerID)
FROM CustomerMaster c
JOIN SalesData s on c.CustomerID = s.CustomerID
--join ItemMaster i on s.item = i.itemcode
GROUP by s.brand, c.division, c.route
) C on A.Brandname = C.Brand and C.Division = A.Division and C.Route = A.Route
See the same on SQL Fiddle
Select B.Brandname,B.Division,C AS DivisionTotalCustomerCount,
ISNULL(T.Count,0) AS PURCHASEDCUSTOMERSCOUNT
from
(
Select CM.Division,M.BrandName,COUNT(distinct CM.CustomerID) AS C
from dbo.CustomerMaster CM
CROSS JOIN ItemMaster M
GROUP BY CM.Division,M.BrandName
)B
LEFT JOIN
(Select Division,Brand,COUNT(Distinct C.CustomerID) As Count from CustomerMaster C
JOIN salesdata D
On C.CustomerID=D.CustomerID
where D.Brand='Red Bull'
GROUP BY Division,Brand
)T
ON B.Brandname=T.Brand
and B.Division=T.Division
Order by 1,2