SQL TO MDX conversion (SUM and Case) - sql

I'm having trouble with simple tasks on MDX since I'm new with it. I basically have a fact table with '1' and '0' on two of my columns. I want a new measure where I can get '1' or '0' if both measures = 1, and sum them to a total but I can't get through it.
This is my query, "BOTH" should be the result I want
select SUM(ASIGNACION_INICIAL), SUM(INICIADO),
SUM(CASE WHEN ASIGNACION_INICIAL = 1 AND INICIADO = 1 THEN 1 ELSE 0 END)
"BOTH",
ID_CAMARA
from EST_MAESTRA_CIVIL group by ID_CAMARA;

Your problem is more related to SSAS project design then MDX. In you SSAS project, go to dsv, there add a calulated column.In this column will implement your logic defined above. Now in your cube use this calculated column as a measure.

Related

Selecting a table field and subtracting a CASE with a SUM function

I'm writing a SQL Command for Crystal Reports and I'm still new to SQL, so I'm having trouble with the following scenario. I've got a SUM function to add up the costs of a production job to get me the actual costs. I want to formulate the margin from the invoice amount and subtract the actual cost from this SUM function to populate another field in my report. When the related invoice has a return applied, it shows as a credit memo type rather than a typical invoice. I want to add a CASE function to the Margin SQL statement.
I'm getting this CASE statement to work to get my actual costs:
,(CASE WHEN t1.[Type] = 'CM' THEN '0.00' ELSE (SELECT SUM(Act_Labor+Act_Material+Act_Service+Act_Labor_Burden+Act_Machine_Burden+Act_GA_Burden) FROM Job WHERE Top_Lvl_Job = t0.Job) END)as Act_Cost
I want to incorporate this CASE statement into my formula to get my Margin as seen below:
,(SELECT ISNULL(t0.Amount, 0) - SUM(Act_Labor+Act_Material+Act_Service+Act_Labor_Burden+Act_Machine_Burden+Act_GA_Burden) FROM Job WHERE Top_Lvl_Job = t0.Job) as Margin
I keep getting errors if I try to use the CASE statement in my Margin formula. I'm not sure where I'm going wrong with the sequence or if my syntax has multiple errors.
I've tried the following syntax, but keep getting errors:
,(SELECT ISNULL(t0.Amount,0) - (CASE WHEN t1.[Type] = 'CM' THEN '0.00' ELSE (SUM(Act_Labor+Act_Material+Act_Service+Act_Labor_Burden+Act_Machine_Burden+Act_GA_Burden) FROM Job WHERE Top_Lvl_Job = t0.Job) END))as Margin
So i figured out the answer to my problem. I changed the CASE statement to give me the information i need, but in a different way.
Rather than starting with the SELECT function to pull the invoice amount then use the CASE function, I changed the query to use the CASE function to populate the invoice amount since that was going to populate anyways based on my subtraction.
Here is the statement I'm using that is working now:
,(CASE WHEN t1.[Type] = 'CM' THEN ISNULL(t0.Amount, 0) ELSE (SELECT ISNULL(t0.Amount, 0) - SUM(Act_Labor+Act_Material+Act_Service+Act_Labor_Burden+Act_Machine_Burden+Act_GA_Burden) FROM Job WHERE Top_Lvl_Job = t0.Job)END) as Margin

Django ORM remove unwanted Group by when annotate multiple aggregate columns

I want to create a query something like this in django ORM.
SELECT COUNT(CASE WHEN myCondition THEN 1 ELSE NULL end) as numyear
FROM myTable
Following is the djang ORM query i have written
year_case = Case(When(added_on__year = today.year, then=1), output_field=IntegerField())
qs = (ProfaneContent.objects
.annotate(numyear=Count(year_case))
.values('numyear'))
This is the query which is generated by django orm.
SELECT COUNT(CASE WHEN "analyzer_profanecontent"."added_on" BETWEEN 2020-01-01 00:00:00+00:00 AND 2020-12-31 23:59:59.999999+00:00 THEN 1 ELSE NULL END) AS "numyear" FROM "analyzer_profanecontent" GROUP BY "analyzer_profanecontent"."id"
All other things are good, but django places a GROUP BY at the end leading to multiple rows and incorrect answer. I don't want that at all. Right now there is just one column but i will place more such columns.
EDIT BASED ON COMMENTS
I will be using the qs variable to get values of how my classifications have been made in the current year, month, week.
UPDATE
On the basis of comments and answers i am getting here let me clarify. I want to do this at the database end only (obviously using Django ORM and not RAW SQL). Its a simple sql query. Doing anything at Python's end will be inefficient since the data can be too large. Thats why i want the database to get me the sum of records based on the CASE condition.
I will be adding more such columns in the future so something like len() or .count will not work.
I just want to create the above mentioned query using Django ORM (without an automatically appended GROUP BY).
When using aggregates in annotations, django needs to have some kind of grouping, if not it defaults to primary key. So, you need to use .values() before .annotate(). Please see django docs.
But to completely remove group by you can use a static value and django is smart enough to remove it completely, so you get your result using ORM query like this:
year_case = Case(When(added_on__year = today.year, then=1), output_field=IntegerField())
qs = (ProfaneContent.objects
.annotate(dummy_group_by = Value(1))
.values('dummy_group_by')
.annotate(numyear=Count(year_case))
.values('numyear'))
If you need to summarize only to one row then you should to use an .aggregate() method instead of annotate().
result = ProfaneContent.objects.aggregate(
numyear=Count(year_case),
# ... more aggregated expressions are possible here
)
You get a simple dictionary of result columns:
>>> result
{'numyear': 7, ...}
The generated SQL query is without groups, exactly how required:
SELECT
COUNT(CASE WHEN myCondition THEN 1 ELSE NULL end) as numyear
-- and more possible aggregated expressions
FROM myTable
What about a list comprehension:
# get all the objects
profane = ProfaneContent.objects.all()
# Something like this
len([pro for pro in profane if pro.numyear=today.year])
if the num years are equal it will add it to the list, so at the and you can check the len()
to get the count
Hopefully this is helpfull!
This is how I would write it in SQL.
SELECT SUM(CASE WHEN myCondition THEN 1 ELSE 0 END) as numyear
FROM myTable
SELECT
SUM(CASE WHEN "analyzer_profanecontent"."added_on"
BETWEEN 2020-01-01 00:00:00+00:00
AND 2020-12-31 23:59:59.999999+00:00
THEN 1
ELSE 0
END) AS "numyear"
FROM "analyzer_profanecontent"
GROUP BY "analyzer_profanecontent"."id"
If you intend to use other items in the SELECT clause I would recommend using a group by as well which would look like this:
SELECT SUM(CASE WHEN myCondition THEN 1 ELSE 0 END) as numyear
FROM myTable
GROUP BY SUM(CASE WHEN myCondition THEN 1 ELSE 0 END)

Filtering based on SSRS total/sum

I am using Visual Studio 2010 and SQL Server 2012.
I have searched the net and the stackoverflow archives and found a few others who have had this problem but I could not get their solutions to work for me. The problem involves filtering based on the aggregate value of a dataset using a user definable report parameter.
I have the following report.
The ultimate goal of this report is to filter portfolios that have a user definable % of cash as a percent of total assets. The default will be 30% or greater. So for example if a Portfolios total market value was $100,000 and their cash asset class was $40,000 this would be a cash perent of 40% and this portfolio would appear on the report.
I have been able to select just the cash asset class using a filter on the dataset itself so that is not an issue. I easily added a cash percent parameter to the dataset but soon realized this is filtering on the row detail not the aggregated sum, and sometimes portfolios have multiple cash accounts. I need the sum of all cash accounts so I can truly know if cash is 30% or greater of total market value.
At first I thought the report was working correctly.
But after cross referencing this against another report I realized this portfolio only has 2.66 % total Cash because it has a large negative balance in a second cash account as well. It's the sum of all cash accounts I care about.
I want to filter portfolios that have >= cash based on the total line not the detail lines. I suspect I may need to alter the dataset using the scalar function SUM() then building a parameter off that, but I have not had success writing a query to do that. I also would be very interested to know if somehow this can be done in the .rdl layer rather than at the sql dataset level. The SQL for this is a little complicated because the program that this is reporting on requries the use of SQL functions, stored procedures, and parameters.
If the solution involves altering the query to include sum() in the dataset itself, I suspect it is line 20 that needs to be summed
a.PercentAssets,
Here is the data set for the report.
https://www.dropbox.com/s/bafdo2i6pfvdkk4/CashPercentDataSet.sql
and here is the .rdl file.
https://www.dropbox.com/s/htg09ypyh7f1a98/cashpercent2.rdl
Thank you
I would do this in SQL personally. You can use a SUM() aggregate to get the value you're after for each account. You won't want to sum the percents though, you'll want to SUM the values and then create the percent afterwards once you have the totals. A sum of averages isn't the same as an average of sums (except in some circumstances but we can't assume that).
I would do something like this, though you'll probably need to tweak it to get it working correctly for you. (I also may not fully understanding the meaning of all the columns you're using).
SELECT PortfolioBaseCode,totalValue,cashValue,
(CAST(cashValue AS FLOAT)/totalValue) * 100 AS pcntCash
FROM (
SELECT b.PortfolioBaseCode,
SUM(a.MarketValue) AS totalValue
SUM(CASE s.SecuritySymbol WHEN 'CASH' THEN a.MarketValue ELSE 0 END) AS cashValue
FROM APXUser.fAppraisal (#ReportData) a
LEFT JOIN APXUser.vPortfolioBaseSettingEx b ON b.PortfolioBaseID = a.PortfolioBaseID
LEFT JOIN APXUser.vSecurityVariant s ON s.SecurityID = a.SecurityID
AND s.SecTypeCode = a.SecTypeCode
AND s.IsShort = a.IsShortPosition
AND a.PercentAssets >= #PercentAssets
GROUP BY b.PortfolioBaseCode
) AS t
WHERE (CAST(cashValue AS FLOAT)/totalValue)>=#pcntParameter
Alternatively you can use the HAVING clause to filter aggregate functions like a WHERE clause would (though I find this a little less readable):
SELECT b.PortfolioBaseCode,
SUM(a.MarketValue) AS totalValue
SUM(CASE s.SecuritySymbol WHEN 'CASH' THEN a.MarketValue ELSE 0 END) AS cashValue,
(SUM(a.MarketValue)/SUM(CASE s.SecuritySymbol WHEN 'CASH' THEN a.MarketValue ELSE 0 END))*100 AS cashPcnt
FROM APXUser.fAppraisal (#ReportData) a
LEFT JOIN APXUser.vPortfolioBaseSettingEx b ON b.PortfolioBaseID = a.PortfolioBaseID
LEFT JOIN APXUser.vSecurityVariant s ON s.SecurityID = a.SecurityID
AND s.SecTypeCode = a.SecTypeCode
AND s.IsShort = a.IsShortPosition
AND a.PercentAssets >= #PercentAssets
GROUP BY b.PortfolioBaseCode
HAVING (SUM(a.MarketValue)/SUM(CASE s.SecuritySymbol WHEN 'CASH' THEN a.MarketValue ELSE 0 END))>=#pcntParameter
Basically you can use grouping and aggregate functions to get the total value and the cash value for each account and then calculate the proper percentage from that. This will take into account any negatives, etc in other holdings.
Some references for aggregate function and grouping:
MSDN - GROUP BY (TSQL)
MSDN - Aggregate Functions
MSDN - HAVING Clause
If you find you're needing to do some kind of aggregation but you cannot group your results you should look into using the TSQL window functions which are very handy:
Working with Window Functions in SQL Server
MSDN - OVER Clause

SQL combining multiple results to one table with Sums

I have the following stored procedure working. It sums up records in a given year (2013) on a specific TIN. The TINS have a Taxpayer name identified with the TIN along with the results.
The columns render out as TIN, TaxpayerName, Total.
This is an accounting based application.
What I would like to do is add a 4th column that will show the sum of another year, so these can be compared to later. The year will always be the preceeding year (2012 in this example).
There may or may not be any entires for the previous year, so I have to give it a 0 or null for the previous year. I am struggling to figure out how to combine them together.
Somehow I am going to have to match each TIN and get the sum of the previous year if it exists.
The portion of this query will look something like this: Trans.Main_AbstractNumber =136 and SUBSTRING(Trans.Main_TaxPeriod,1,4) = '2012', Using SUM(Main_AbstractAmount) as PreviousTotal or something like that.
Does anyone have any idea how I should approach this?
Thank you
Select Trans.Main_TIN, C.TaxpayerName, SUM(Main_AbstractAmount) as Total
from qetl.RECORDS Trans join qetl.TAXPAYERS C on Trans.Main_TIN=C.TIN
where Trans.Main_AbstractNumber =136 and SUBSTRING(Trans.Main_TaxPeriod,1,4) = '2013'
GROUP BY Trans.Main_TIN,C.TaxpayerName
You want to use conditional summation:
Select Trans.Main_TIN, C.TaxpayerName,
SUM(case when SUBSTRING(Trans.Main_TaxPeriod,1,4) = '2013' then Main_AbstractAmount end) as Total2013,
SUM(case when SUBSTRING(Trans.Main_TaxPeriod,1,4) = '2012' then Main_AbstractAmount end) as Total2012
from qetl.RECORDS Trans join
qetl.TAXPAYERS C
on Trans.Main_TIN=C.TIN
where Trans.Main_AbstractNumber = 136
GROUP BY Trans.Main_TIN, C.TaxpayerName ;

Filtering a Measure (or Removing Outliers)

Say I have a measure, foo, in a cube, and I have a reporting requirement that users want to see the following measures in a report:
total foo
total foo excluding instances where foo > 10
total foo excluding instances where foo > 30
What is the best way to handle this?
In the past, I have added Named Calculations which return NULL if foo > 10 or just foo otherwise.
I feel like there has to be a way to accomplish this in MDX (something like Filter([Measures].[foo], [Measures].[foo] > 10)), but I can't for the life of me figure anything out.
Any ideas?
The trick is that you need to apply the filter on your set, not on your measure.
For example, using the usual Microsoft 'warehouse and sales' demo cube, the following MDX will display the sales for all the stores where sales were greater than $2000.
SELECT Filter([Store].[Stores].[Store].members, [Unit Sales] > 2000) ON COLUMNS,
[Unit Sales] ON ROWS
FROM [Warehouse and Sales]
I met similar problem when use saiku (backend with Mondrain), as I haven't found any clear solution of "add filter on measure", I added it here, and that may be useful for other guy.
In Saiku3.8, you could add filter on UI: "column"->"filter"->"custom", then you may see a Filter MDX Expression.
Let's suppose we want clicks in Ad greater than 1000, then add the following line there:
[Measures].[clicks] > 1000
Save and close, then that filter will be valid for find elem with clicks greater than 1000.
The MDX likes below (suppose dt as dimension and clicks as measure, we want to find dt with clicks more than 1000)
WITH
SET [~ROWS] AS
Filter({[Dt].[dt].[dt].Members}, ([Measures].[clicks] > 1000))
SELECT
NON EMPTY {[Measures].[clicks]} ON COLUMNS,
NON EMPTY [~ROWS] ON ROWS
FROM [OfflineData]
i think you have two choices:
1- Add column to your fact(or view on data source view that is based on fact table)like:
case when unit_Price>2000 then 1
else 0
end as Unit_Price_Uper_Or_Under_10
and add a fictitious Dimension based on this columns value.
and add named query for New Dimension(say Range_Dimension in datasourceview :
select 1 as range
union all
select 0 as range
and after taht you cant used this filter like other dimension and attribute.
SELECT [Store].[Stores].[Store].members ON COLUMNS,
[Unit Sales] ON ROWS
FROM [Warehouse and Sales]
WHERE [Test_Dimension].[Range].&[1]
the problem is for every range you must add When condition and only if the range is static this solution is a good solution.
and for dynamic range it's better to formulate the range (based on disceretizing method )
2- add dimension with granularity near fact table based on fact table
for example if we have fact table with primary key Sale_id.we can add
dimension based on fact table with only one column sale_Id and in dimension Usage tab
we can relate this new dimension and measure group with relation type Fact and
after that in mdx we can use something like :
filter([dim Sale].[Sale Id].[Sale Id].members,[Measures].[Unit Price]>2000)