How do I get sum of a column with different where conditions on the same table in rails? - sql

I have method which calculates total income from receipts table. But currently it makes two sql calls every time the method is called, is it possible to improve the sql to make only one database call. I am fine with raw sql if needed.
def total
Receipt.where(receipt_type: 'income').sum(:amount) - Receipt.where(receipt_type: 'refund').sum(:amount)
end
Main objective is avoid two database calls. And if the difference can be calculated in the database only it will be even better. Thanks.

In SQL, I would solve this with
SELECT SUM(amount) FILTER (WHERE receipt_type = 'income')
- SUM(amount) FILTER (WHERE receipt_type = 'refund')
FROM receipt
but I wasn't able to find the equivalent in ruby. I have no experience with it, but from this example, I think the following should work:
Receipt.select("SUM(amount) FILTER (WHERE receipt_type = 'income') - SUM(amount) FILTER (WHERE receipt_type = 'refund') )

Related

Running Sum Query

I'm new to Access and I'm trying to develop my own Inventory Management Database.
I'm trying to make a query that could display a running total of the Inventory on Hand as of a specific date. This is how my table looks:
It's sorted according to ITEM_ID then TRANDATE in ascending order. I'd like to add a calculated field beside the NET field that would show a running total of the specific ITEM_ID after a specific date. Negative numbers in the NET field represent a sale while the positive ones represent a purchase. I tried using the DSUM function as it is widely recommended in creating a running sum field. My expression is this
DSum([NET],"InvtyTransT", "[ITEM_ID]=" & [ITEM_ID] And "[TRANDATE]<=#" & [TRANDATE] & "#"). But it only shows the total of the NET field (6827) in each record like this:
What I needed is like this:
(I used an IF function in excel to compute this)
Please help. I think I might have missed something in my expression. I've tried revising it several times and it would always give me the same wrong answer in every record.
Thanks in advance.
Try correlated sub-query.
SELECT t.*, (SELECT SUM(t2.NET)
from InvtyTransT as t2
WHERE (t2.TRANDATE <= t.TRANDATE AND t2.ITEM_ID = t.ITEM_ID)
) AS rSUM
FROM InvtyTransT AS t;

SQL months_between grouping issue

First post; go easy on me.
Relatively new to SQL (anything beyond simple queries really), but attempting to learn more complex functions in an effort to take advantage of superior server resources. My issue:
I would like to use a SUM function to aggregate cash flows across a very large variety of sources. I would like to see these cash flows along a monthly time period. Because the cash flows start at different times, I would like to season them so that they are all aligned. My current code:
select
months_between(A.reporting_date, B.start_date) as season,
sum(case when A.current_balance is null then B.original_balance
else A.current_balance end) as cashflow
from dataset1 A, dataset2 B
group by season
order by season
Now, executing the code like this generates an error message that states that A.reporting_date and B.start_date must be GROUPED or part of an AGGREGATE function.
The problem is, if I add them to the GROUP BY statement, while it generates output without error, I get cash flow sums that are essentially Cartesian crosses with all the grouped variables.
So long story short, is there any way for me to get cash flow sums grouped by only the season? If so, any ideas how to do it?
Thank you.
Most databases don't allow using column aliases defined previously, in where, group by and order by clauses.
For your query you should use months_between(A.reporting_date, B.start_date) instead of the alias season in group by and order by.
Also your query will return a cross product, as a join condition isn't specified.
select
months_between(A.reporting_date, B.start_date) as season,
sum(case when A.current_balance is null then B.original_balance
else A.current_balance end) as cashflow
from dataset1 A
JOIN dataset2 B ON --add a join condition
group by months_between(A.reporting_date, B.start_date)
order by months_between(A.reporting_date, B.start_date)

Calculate First Time Buyer and Repeating Buyers using MAQL Queries in GOOD-DATA platform

I have been recently working with GOOD-DATA platform. I don't have that much experience in MAQL, but I am working on it. I did some metric and reports in GOOD-DATA platform. Recently I tried to create a metric for calculating Total Buyers,First Time Buyers and Repeating Buyers. I created these three reports and working perfect.But when i try to add a order date parent filter the first time buyers and repeating buyers value getting wrong. please have Look at Following queries.
I can find out the correct values using sql queries.
MAQL Queries:
TOTAL ORDERS-
SELECT COUNT(NexternalOrderNo) BY CustomerNo WITHOUT PF
TOTAL FIRSTTIMEBUYERS-
SELECT COUNT(CustomerNo) WHERE (TOTAL ORDER WO PF=1) WITHOUT PF
TOTAL REPEATINGBUYERS-
SELECT COUNT(CustomerNo) WHERE (TOTAL ORDER WO PF>1) WITHOUT PF
Can any one suggest a logic for finding these values using MAQL
It's not clear what you want to do. If you could provide more details about the report you need to get, it would be great.
It's not necessary to put "without pf" into the metrics. This clause bans filter application, so when you remove it, the parent filter will be used there. And you will probably get what you want. Specifically, modify this:
SELECT COUNT(CustomerNo) WHERE (TOTAL ORDER WO PF>1) WITHOUT PF
to:
SELECT COUNT(CustomerNo) WHERE (TOTAL ORDER WO PF>1)
The only thing you miss here is "ALL IN ALL OTHER DIMENSIONS" aka "ALL OTHER".
This keyword locks and overrides all attributes in all other dimensions—keeping them from having any affect on the metric. You can read about it more in MAQL Reference Guide.
FIRSTTIMEBUYERS:
SELECT COUNT(CustomerNo)
WHERE (SELECT IFNULL(COUNT(NexternalOrderNo), 0) BY Customer ID, ALL OTHER) = 1
REPEATINGBUYERS:
SELECT COUNT(CustomerNo)
WHERE (SELECT IFNULL(COUNT(NexternalOrderNo), 0) BY Customer ID, ALL OTHER) > 1

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

Calculate result from multi table in database

I have a question in my VB.NET POS Development and could't find a solution myself. I'm currently using VS2010 and MS Access as my database.
I have two database table as shown below:
SalesReceipt(#Receipt_ID, Sales_Date, Receipt_Number, Customer_ID, Sales_BDiscount, Sales_ADiscount, Sales_Payment)
Customer(#Customer_ID, Customer_Name, Customer_Contact, Customer_Debt)
NOTE : BDiscount = Before Discount / ADiscount = After Discount
In my system, one customer can have many SalesReceipt. Now my problem is how can I update the correct Customer_Debt for customer? My logic is update the respective customer's Customer_Debt by looping every row in SalesReceipt and calculate the debt by doing something like :
totalDebt = totalDebt + (Sales_Payment - Sales_ADiscount)
But I not sure how can I make sure it only loop and calculate the result for the selected customer only. How can I achieve this correctly in .NET?
If you want to calculate totalDebt per customer you can use query
SELECT Customer_ID, sum(Sales_Payment - Sales_ADiscount) as totalDebt FROM SalesReceipt
GROUP BY Customer_ID
Result contains totalDebts aggregated by Customer_ID and can be used to update Customer (all the looping and calculating is done by a database engine).
The query can also update be more complex to do even update for you.
Couldn't you just write a query in your Access db that performs your calculation (Sales_Payment - Sales_ADiscount) on your SalesReceipt table grouped by CustomerID?