selecting max of distinct count when on multiple rows - ssas

I have a query that does a distinct count of accounts based on CB score. The problem I have is that an account can switch credit scores pretty often. So I would like to take the max credit score for a particular account.
Here is what I have that is returning higher than I want because of the switching of CB scores.
So for example account 1234 initially has a cb score of 500, then later has a cb score of 550. The account 1234 is now in both cb score ranges, but I only want to count them once.
WITH
MEMBER mem1 AS
Count
(
NonEmpty
(
[AccountID].[Account ID].[Account ID].MEMBERS
,measures.[Transaction Amount]
)
)
SELECT
NON EMPTY
{mem1} ON 0
,[CB Score].[RANGE].[RANGE] ON 1
FROM [CUBE]
WHERE [PROMO].[PROMO].&24

Try this:
WITH
MEMBER mem1 AS
Sum
(
[AccountID].[Account ID].[Account ID].MEMBERS
,IIF(
Not IsEmpty([Measures].[Transaction Amount])
And IsEmpty(Sum({[CB Score].[RANGE].CurrentMember.NextMember : null}, [Measures].[Transaction Amount])),
1,
Null
)
)
SELECT
NON EMPTY
{mem1} ON 0
,[CB Score].[RANGE].[RANGE] ON 1
FROM [CUBE]
WHERE [PROMO].[PROMO].&24
Basically the logic is to count the account if they had a transaction in that slice and if no greater CB Score range has a transaction.

Related

Get count With Distinct in SQL Server

Select
Count(Distinct iif(t.HasReplyTask = 1, t.CustomerID, Null)) As Reply,
Count(Distinct iif(t.HasOverdueTask = 1, t.CustomerID, Null)) As Overdue,
Count(Distinct t.CustomerID) As Total
From
Table1 t
If a customer is in Reply, we need to remove that customer in Overdue count, That means if Customer 123 is in both, The Overdue count should be one less. How can I do this?
I am adding some data here,
Customer 123 has "HasReplyTask", so, we have to filter that customer from Count in OverDue(even though that customer has one Overdue task without HasReplyTask). 234 is one and Distinct of 456 is one.
So, the overdue count should be 2, Above query returns 3
If I've got it right, this can be done using a subquery to get the numbers for each customer, and then get the summary information as follows:
Select Sum(HasReplyTask) As Reply,
Sum(HasOverdueTask) As Overdue,
Count(CustomerID) As Total
From (
Select CustomerID,
IIF(Max(Cast(HasReplyTask As TinyInt))<>0, 0, Max(Cast(HasOverdueTask As TinyInt))) As HasOverdueTask,
Max(Cast(HasReplyTask As TinyInt)) As HasReplyTask
From Table1
Group by CustomerID) As T
I don't know about column data types, so I used cast function to use max function.
db<>fiddle
Reply
Overdue
Total
1
2
3
What would probably be more efficient for you is to pre-aggregate your table by customer ID and have counts per customer. Then your outer query can test for whatever you are really looking for. Something like
select
sum( case when PQ.ReplyCount > 0 then 1 else 0 end ) UniqReply,
sum( case when PQ.OverdueCount > 0 then 1 else 0 end ) UniqOverdue,
sum( case when PQ.OverdueCount - PQ.ReplyCount > 0 then 1 else 0 end ) PendingReplies,
count(*) as UniqCustomers
from
( select
yt.customerid,
count(*) CustRecs,
sum( case when yt.HasReplyTask = 1 then 1 else 0 end ) ReplyCount,
sum( case when yt.HasOverdueTask = 1 then 1 else 0 end ) OverdueCount
from
yourTable yt
group by
yt.customerid ) PQ
Now to differentiate the count you are REALLY looking for, you might need to do a test against the prequery (PQ) of ReplyCount vs OverdueCount such as... For a single customer ID (thus the pre query), if the OverdueCount is GREATER than the ReplyCount, then it is still considered overdue? So for customer 123, they had 3 overdue, but only 2 replies. You want that counted once? But for customers 234 and 456, the only had overdue entries and NO replies. So, the total where Overdue - Reply > 0 = 3 distinct people.
Is that your ultimate goal?

MDX query total sell price of rows with more than 10 quantity

I need to have a total sell price of rows that have more than 10 Quantity.
The result of the following query is the total sell price.
SELECT {[Sell Price]} ON COLUMNS
FROM SALES
I have to filter the above query with the condition ([Sell Quantity] > 10) for bellow dimensions.
[Customer].[Customer Name].[Customer Name]
[Goods].[Goods Name].[Goods Name]
Thanks in advance
EDIT
Example data is like bellow
Customer Goods Quantity Price
-------------------------------------------------
A X 2 1000
B X 15 2000
C Y 20 3000
C X 3 6000
Customers and Goods are dimensions. I need a total price of second and third rows because quantity of these rows is more than 10.
My expected result is 5000.
You need to use filter function. Based on the schema shared above your query will be
Select
{
[Sell Price]
}on columns
from yourCube
where
{filter(
([Customer].[Customer Name].[Customer Name],[Goods].[Goods Name].[Goods Name]),
[Sell Quantity] > 10)}

I need to filter this MDX result set

I am looking to filter the result set below such that I only show results where Dimension A Value 1 have a count of 1, regardless of the value of count for Dimension A Value 2
Dimension A Value 1 Dimension A Value 2
Entity ID Count Count
11 1
78 1
90 1
101 1
114 1
118 1
125 1
134 1
140 1
161 1
169 1
186 1 2
The filtered set would look like
Dimension A Value 1 Dimension A Value 2
Entity ID Count Count
11 1
78 1
90 1
101 1
118 1
125 1
140 1
161 1
169 1
186 1 2
the mdx is
WITH
SET [~COLUMNS] AS
{[Dimension A].[Dimension A].[Value 1], [Dimension A].[Dimension A].[Value 2]}
SET [~ROWS] AS
{[Entity].[Entity].[Entity ID].Members}
SELECT
NON EMPTY CrossJoin([~COLUMNS], {[Measures].[Count]}) ON COLUMNS,
NON EMPTY [~ROWS] ON ROWS
FROM [My Cube]
I've been playing around with Filter and NonEmpty but I'm new to MDX and my sql brain is hurting. I suppose this is probably trivial to someone with a lot of MDX under their belt but I'm failing. Be gentle this is my first question
You can try a HAVING clause:
WITH
SET [~COLUMNS] AS
{
[Dimension A].[Dimension A].[Value 1],
[Dimension A].[Dimension A].[Value 2]
}
MEMBER [Measures].[CountValue1] AS //<<<<this is new <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
(
[Measures].[Count],
[Dimension A].[Dimension A].[Value 1]
)
SELECT
NON EMPTY
CrossJoin(
[~COLUMNS]
, {[Measures].[Count]}
) ON COLUMNS,
NON EMPTY
[Entity].[Entity].[Entity ID].MEMBERS
HAVING [Measures].[CountValue1] = 1 //<<CHANGED TO NEW MEASURE
ON ROWS
FROM [My Cube];
If you can use HAVING rather than FILTER you will likely see a performance improvement - particularly if your scripts become more complex:
https://blog.crossjoin.co.uk/2006/01/04/the-having-clause/
Just to be complete the slower FILTER version would be this:
WITH
SET [~COLUMNS] AS
{
[Dimension A].[Dimension A].[Value 1],
[Dimension A].[Dimension A].[Value 2]
}
//>>>>>> following is new >>>>>>>>>>>>>>>>>>>>>
MEMBER [Measures].[CountValueNEW] AS
(
[Measures].[Count],
[Dimension A].[Dimension A].[Value 1]
)
SELECT
NON EMPTY
[~COLUMNS]
*{[Measures].[Count]}
ON 0,
NON EMPTY
FILTER(
[Entity].[Entity].[Entity ID].MEMBERS,
[Measures].[CountValueNEW] = 1
)
ON 1
FROM [My Cube];
Your query should be like
Select ([Dimension A].[AttributeHierarchy1].[AttributeHierarchy1],{[Measures].[Value1],[Measures].[Value2]}) on columns,
filter([Dimension B].[EntityID].[EntityID],[Measures].[Value1]=0)
on rows
from yourcube
However there can be an issue. For example your dimesnion has two values A and B, for a particular row , A,value1 =1 but B, Value1=0, this row will appear as A qulifies for it and B gets carried over.
Edit
Lets take an example I want to see Internet Sales for Bottles and Cages haveing sales more than 150$
My initial query
select
([Product].[Subcategory].[Subcategory],[Measures].[Internet Sales Amount]
)
on columns,
[Customer].[City].[City]
on rows
from
[Adventure Works]
Result
Now modify the query
select
([Product].[Subcategory].[Subcategory],[Measures].[Internet Sales Amount]
)
on columns,
filter
(
[Customer].[City].[City], [Measures].[Internet Sales Amount]>150
)
on rows
from
(select [Product].[Subcategory].&[28] on 0 from [Adventure Works])
Result

Calculate percentages of columns in Oracle SQL

I have three columns, all consisting of 1's and 0's. For each of these columns, how can I calculate the percentage of people (one person is one row/ id) who have a 1 in the first column and a 1 in the second or third column in oracle SQL?
For instance:
id marketing_campaign personal_campaign sales
1 1 0 0
2 1 1 0
1 0 1 1
4 0 0 1
So in this case, of all the people who were subjected to a marketing_campaign, 50 percent were subjected to a personal campaign as well, but zero percent is present in sales (no one bought anything).
Ultimately, I want to find out the order in which people get to the sales moment. Do they first go from marketing campaign to a personal campaign and then to sales, or do they buy anyway regardless of these channels.
This is a fictional example, so I realize that in this example there are many other ways to do this, but I hope anyone can help!
The outcome that I'm looking for is something like this:
percentage marketing_campaign/ personal campaign = 50 %
percentage marketing_campaign/sales = 0%
etc (for all the three column combinations)
Use count, sum and case expressions, together with basic arithmetic operators +,/,*
COUNT(*) gives a total count of people in the table
SUM(column) gives a sum of 1 in given column
case expressions make possible to implement more complex conditions
The common pattern is X / COUNT(*) * 100 which is used to calculate a percent of given value ( val / total * 100% )
An example:
SELECT
-- percentage of people that have 1 in marketing_campaign column
SUM( marketing_campaign ) / COUNT(*) * 100 As marketing_campaign_percent,
-- percentage of people that have 1 in sales column
SUM( sales ) / COUNT(*) * 100 As sales_percent,
-- complex condition:
-- percentage of people (one person is one row/ id) who have a 1
-- in the first column and a 1 in the second or third column
COUNT(
CASE WHEN marketing_campaign = 1
AND ( personal_campaign = 1 OR sales = 1 )
THEN 1 END
) / COUNT(*) * 100 As complex_condition_percent
FROM table;
You can get your percentages like this :
SELECT COUNT(*),
ROUND(100*(SUM(personal_campaign) / sum(count(*)) over ()),2) perc_personal_campaign,
ROUND(100*(SUM(sales) / sum(count(*)) over ()),2) perc_sales
FROM (
SELECT ID,
CASE
WHEN SUM(personal_campaign) > 0 THEN 1
ELSE 0
end AS personal_campaign,
CASE
WHEN SUM(sales) > 0 THEN 1
ELSE 0
end AS sales
FROM the_table
WHERE ID IN
(SELECT ID FROM the_table WHERE marketing_campaign = 1)
GROUP BY ID
)
I have a bit overcomplicated things because your data is still unclear to me. The subquery ensures that all duplicates are cleaned up and that you only have for each person a 1 or 0 in marketing_campaign and sales
About your second question :
Ultimately, I want to find out the order in which people get to the
sales moment. Do they first go from marketing campaign to a personal
campaign and then to sales, or do they buy anyway regardless of these
channels.
This is impossible to do in this state because you don't have in your table, either :
a unique row identifier that would keep the order in which the rows were inserted
a timestamp column that would tell when the rows were inserted.
Without this, the order of rows returned from your table will be unpredictable, or if you prefer, pure random.

SQL SUM problems

I have this Query:
Select Denumire,Sum(Livrari.Cantitate)as Stoc,
isnull(Sum(Facturi_Emise.Cantitate),0) as 'Sold',
isnull(Sum(Livrari.Cantitate),0)-isnull(sum(Facturi_Emise.Cantitate),0) As 'Stoc After Sales',
Livrari.Id_Depozit
From Livrari
Left join Produse On Livrari.Id_Produs=Produse.Id_Produs
Left join Facturi_Emise On Produse.Id_Produs=Facturi_Emise.Id_Produs
group by Denumire, Livrari.Id_Depozit
order by Denumire
I need to get the Stock, the quantity of the items that are required to be sold, and what I got in the 2 deposits after the items are sold. But in the table "Livrari" I got the same item in both deposit, with the amount of 300 for deposit 1, and 300 for deposit 2 and when I make the sum and group by Id_Depozit I get that item with the amount of 600 in deposit 1 and 600 in deposit 2, same is for table Facutri_Emise as I should take only 250 items from deposit 1 and 300 from deposit 2, it takes 550(250+300) from both deposit and I got the Stoc after Sale 50 for each, instead of 50 for deposit 1 and 0 for deposit 2.
fiddle
My problem is that the result in row 8 and 9(that means Pahare) in column Stoc should be 300 for each. In next column Sold, row 8 should be 250 and for row 9 300. In the end, in column Stoc after Sale should be the difference, as row 8=50, row 9=0.
I mean each time a products repeats, my query is gonna make the sum and put the same value in deposit 1 and in deposit 2, as I give different values for each deposit it should show me the correspondent values. A supplier will bring a certain amount if items(let's say apples) into deposit 1 and another supplier will bring another amount of apples in deposit 2. When the bill is made("Facturi_Emise") for the client I should sent an amount of apples from deposit 1 and another from deposit 2 and I wanna see what amount remains in one deposit,and what remains in other one.
Here it is.
It is not ordered by deposit numbers,but it's okey.
SELECT Produse.Denumire,
Isnull(a.suma,0) AS Intrari,
Isnull(b.suma,0) AS Iesiri,
isnull(a.suma,0) - isnull(b.suma,0) AS Stoc
FROM Produse
LEFT JOIN
(SELECT ID_Produs, Sum(Cantitate) AS suma FROM Livrari GROUP BY ID_Produs)
AS a
ON Produse.ID_Produs = a.ID_Produs
LEFT JOIN
(SELECT ID_Produs, Sum(Cantitate) AS suma FROM Facturi_Emise GROUP BY ID_Produs)
AS b
ON Produse.ID_Produs = b.ID_Produs
SOLLUTION
Select Denumire,Sum(Livrari.Cantitate)as Stoc,
SUM(ISNULL(Facturi_Emise.Cantitate,0)) as 'Sold',
SUM(ISNULL(Livrari.Cantitate,0))-SUM(ISNULL(Facturi_Emise.Cantitate,0)) As 'Stoc After Sales',
Livrari.Id_Depozit
From Livrari
Left join Produse On Livrari.Id_Produs=Produse.Id_Produs
Left join Facturi_Emise On Produse.Id_Produs=Facturi_Emise.Id_Produs
and Livrari.Id_Depozit=Facturi_Emise.Id_Depozit
group by Denumire, Livrari.Id_Depozit
order by Denumire
You need ceck null before sum, soo first ISNULL and then SUM.
If you SUM and one is null then all SUM will be null.