Somar um valor total e retornar muitas linhas - sql

I have two tables (order header and order lines). I want to add the order lines, group by month and year, but it brings me all the rows of all the orders. Have you any way to sum it up?
This is probably because I'm doing one calculation per line.
I need to do it per line because the stock is from a few different places.
For example, one stock is from the back street and another stock is from the other block.
If I use the total discount for the sales order, it doubles the same order if the inventory comes from different places.
and if I use the calculation to make the discount per line, it will bring line by line (for sure).
Was there any other possibility?
I tried with variable, but I can not pass more than one value on the same variable.
SELECT
MONTH(X.DOCDATE) MES,
YEAR(X.DOCDATE) ANO,
(100 - X.DiscPrcnt)* SUM(LineTotal) /100 as 'TOTAL'
FROM RDR1 INNER JOIN ORDR X ON RDR1.DocEntry = X.DocEntry
WHERE X.CANCELED <> 'Y'
AND X.DocTotal > 0
AND X.DocDate BETWEEN '20140101' AND '20190630'
AND OcrCode IN ('EXT', 'EXT-JD')
Group by
X.DOCDATE,
X.DiscPrcnt
ORDER BY X.DOCDATE
I would like it to be:
Month YEAR Total
1 2014 5000
2 2014 7000

I imagine you want to be grouping by month and year rather than the full date. Also, I think you may not want a separate row for each discount percent value right?
How about this query?:-
SELECT
MONTH(X.DOCDATE) MES,
YEAR(X.DOCDATE) ANO,
SUM((100 - X.DiscPrcnt)* X.LineTotal/100) as 'TOTAL'
FROM RDR1 INNER JOIN ORDR X ON RDR1.DocEntry = X.DocEntry
WHERE X.CANCELED <> 'Y'
AND X.DocTotal > 0
AND X.DocDate BETWEEN '20140101' AND '20190630'
AND OcrCode IN ('EXT', 'EXT-JD')
Group by
YEAR(X.DOCDATE),
MONTH(X.DOCDATE)
ORDER BY
YEAR(X.DOCDATE),
MONTH(X.DOCDATE)

Related

Return data where the running total of amounts 30 days from another row is greater than or equal to the amount for that row

Let's say I a table that contains the date, account, product, purchase type, and amount like below:
Looking at this table, you can see that for any particular account/product combination, there are buys and sells. Essentially, what I'd like to write is a SQL query that flags the following: Are there accounts that bought at a certain amount and then sold the same aggregate amount or more 30 days from that buy?
So for example, we can see account 1 bought product A for 20k on 8/1. If we look at the running sum of sells by account 1 for product A over the next 30 days, we see they sold a total of 20k - the same as the initial buy:
Ideally, the query would return results that flag all of these instances: for each individual buy, find all sells for that product/account 30 days from that buy, and only return rows where the running total of sells is greater than or equal to that initial buy.
EDIT: Using the sample data provided, the desired should look more or less look like the following:
You'll see that the buy on 8/2 for product B/account 2 is not returned because the running sum of sells for that product/account/buy combination over the next 30 days does not equal or exceed the buy amount of 35k but it does return rows for the buy on 8/3 for product B/ account 2 because the sells do exceed the buy amount of 10k.
I know I need to self join the sells against the buys, where the accounts/products equal and the datediff is less than or equal 30 and I basically have that part structured. What I can't seem to get working is the running total part and only returning data when that total is greater than or equal to that buy. I know I likely need to use the over/partition by clauses for the running sum but I'm struggling to produce the right results/optimize properly. Any help on this would be greatly appreciated - just looking for some general direction on how to approach this.
Bonus: Would be even more powerful to stop returning the sells once the running total passes the buy, so for example, the last two rows in the desired output I provided aren't technically needed - since the first two sells following the buy had already eclipsed the buy amount.
In SQL Server, one option uses a lateral join:
select
t.*,
case when t.amount = x.amount then 1 else 0 end as is_returned
from mytable t
cross apply (
select sum(amount) amount
from mytable t1
where
t1.purchase_type = 'Sell'
and t1.account = t.account
and t1.product = t.product
and t1.date >= t.date
and t1.date <= dateadd(day, 30, t.date)
) x
where t.purchase_type = 'Buy'
The lateral join sums the amount of "sells" of the same account and product within the following 30 days, which you can then compare with the amount of the buy. The query gives you one row per buy, with a boolean flag that indicates if the amounts match.
In databases that support the range specification to window functions, this would be more efficiently expressed with a window sum:
select *
from (
select
t.*,
case when amount = sum(case when purchase_type = 'Sell' then amount end) over(
partition by account, product
order by date
range between current row and interval '30' day following
) then 1 else 0 end as is_returned
from mytable t
) t
where purchase_type = 'Buy'
Edit: this would generate a resultset similar to the third table in your question:
select t.*, x.*
from mytable t
cross apply (
select
t1.date sale_date,
t1.amount sell_amount,
sum(t1.amount) over(order by t1.date) running_sell_amount,
sum(t1.amount) over() total_sell_amount
from mytable t1
where
t1.purchase_type = 'Sell'
and t1.account = t.account
and t1.product = t.product
and t1.date >= t.date
and t1.date <= dateadd(day, 30, t.date)
) x
where t.purchase_type = 'Buy' and t.amount = x.total_sell_amount

Where clause within Case Statement

I need to drill into detail on to an existing report which shows me the total profitability per customer, per month on a telecoms company.
The company sells calls, service charges and has discounts.
This is the query that I'm using, using one customer in particular:
SELECT
custumer_name,
ISNULL(SUM(CASE CONVERT(VARCHAR(10), month_end, 103)
WHEN '31/10/2016'
THEN totcall + isnull(totrec, 0) - discount
ELSE 0
END), 0) AS '31/10/2016',
SUM(totcall) AS 'Total Calls',
SUM(totrec) AS 'Total Rec',
SUM(discount) AS 'Discounts'
FROM
total_sales
INNER JOIN
customer_details ON billingaddress = customer_details .siteid
INNER JOIN
sales_month d ON total_sales.periodid = d.monthid
INNER JOIN
customer b ON customer_details .id = b.id AND b.is_customer = 1
WHERE
b.custumer_name = '2
GROUP BY
b.custumer_name
ORDER BY
b.custumer_name ASC
This is bringing back the correct total however when I need to show the drilldown of the total on calls, rec & discounts it is showing me the actual sum of every months' data which is stored on that table.
I'm not sure how can I get the last 3 columns to itemise the total without specifying the actual month (as the report has columns for the last 12 months of data.
Please help?
Thank you!
PS. The DB is a SQL Server 2008

Revenue year by year SQL Server query

I have the following query which provides me with the item and item details, values, rate and quantity across each location.
I am trying to get the yearly revenue based on the Start and End Date. Example, if the chosen date was 2013-2015. The final result will create 3 columns one for 2013 revenue, one for 2014 revenue and one for 2015 revenue.
I am a newbie and still not an expert in writing queries, but here is what I have currently:
SELECT
department,
item,
itemdesc,
qty1,
qty2,
rate_1,
rate_2,
SUM(mm.days*mm.rate*mm.qty)
FROM
items it
LEFT JOIN
(SELECT
i.days, i.rate, i.days, ii.todate, ii.itemid
FROM
invoiceofitems ii
JOIN
invoices i on i.id = ii.id
WHERE
ii.todate BETWEEN #StartDate and #EndDate) mm ON mm.itemid = it.itemid
GROUP BY
department,
item,
itemdesc,
qty1, qty2,
rate_1, rate_2
ORDER BY
item
However, this does not provide me with a year to year aggregation of invoice revenue that I require.
I know this is possible to achieve via iterating through this. But how would I accomplish this and where would I start on this?
Would I need to know the start and end date of each year and iterate through that and then add a counter to the year until year= EndDate?
I'm extremely confused. Help would be appreciated.
I hope that PIVOT and YEAR help you to solve this problem (some columns are omitted):
;WITH SRC(department,item, ... , rate_2, yr, calculation) AS
(SELECT it.department, it.item, ..., it.rate_2, YEAR(ii.todate) as yr,
(i.days * i.rate *i.qty) as calculation
FROM items it
LEFT JOIN invoiceofitems ii ON ii.itemid = it.itemid
JOIN invoices i ON i.id = ii.id)
SELECT department,item, ..., [2013],[2014],[2015]
FROM SRC
PIVOT
(SUM(calculation) FOR yr IN ([2013],[2014],[2015])) PVT
The YEAR function returns only 'year' part of your date and makes grouping easier. PIVOT just rotates grouped data from rows to columns.

PRINT only SUM by year (group by Territory) in SQL

SELECT year(soh.OrderDate) 'year',sum(soh.TotalDue) 'Total',st.[Group] TerritoryGroup
FROM Sales.SalesOrderHeader soh
LEFT OUTER JOIN Sales.SalesTerritory st
ON soh.TerritoryID=st.TerritoryID
GROUP BY year(soh.OrderDate),(soh.TotalDue),[Group]
ORDER BY year(soh.OrderDate),(soh.TotalDue)
This is what I came up with, but the years are scattered instead of ONE year per Territory total.
(I like to print the Total for each year in each Territory)
Is there a concise way to make this select statement?
If you want one row per year, then only include that in the group by:
SELECT year(soh.OrderDate) as year, sum(soh.TotalDue) as Total
FROM Sales.SalesOrderHeader soh LEFT OUTER JOIN
Sales.SalesTerritory st
ON soh.TerritoryID = st.TerritoryID
GROUP BY year(soh.OrderDate)
ORDER BY year(soh.OrderDate);
If you want one row per year and territory group, then include only those two columns:
SELECT year(soh.OrderDate) as year, sum(soh.TotalDue) as Total, st.[Group] as TerritoryGroup
FROM Sales.SalesOrderHeader soh LEFT OUTER JOIN
Sales.SalesTerritory st
ON soh.TerritoryID = st.TerritoryID
GROUP BY year(soh.OrderDate), [Group]
ORDER BY year(soh.OrderDate), Total;
Some notes:
You do not need single quotes around the column aliases. You should use single quotes only for string and date constants.
If you are summarizing just by year, then you cannot have TerritoryGroup in the output.
In neither case would you include soh.TotalDue in the group by. You are summing that column, not aggregating by it.
The order by clause should not contain soh.TotalDue; it should be the aggregated value (Total) instead.
In GROUP BY you say you want one line per combination of year, totaldue and territory group.
Let's say you have these records:
orderdate totaldue territorygroup
2014-01-01 100 1
2014-01-15 200 1
2014-01-21 100 1
2013-03-03 100 1
2014-04-04 100 2
Then you get these result records:
year totaldue territorygroup
2014 100 1
2014 200 1
2013 100 1
2014 100 2
(BTW: sum(soh.TotalDue) = soh.TotalDue, because you group by TotalDue.)
So the solution for you is to say what you want to see in your result records actually. One record per ______. Thus you get your GROUP BY clause and the results you want.

SQL query to identify seasonal sales items

I need a SQL query that will identify seasonal sales items.
My table has the following structure -
ProdId WeekEnd Sales
234 23/04/09 543.23
234 30/04/09 12.43
432 23/04/09 0.00
etc
I need a SQL query that will return all ProdId's that have 26 weeks consecutive 0 sales. I am running SQL server 2005. Many thanks!
Update: A colleague has suggested a solution using rank() - I'm looking at it now...
Here's my version:
DECLARE #NumWeeks int
SET #NumWeeks = 26
SELECT s1.ProdID, s1.WeekEnd, COUNT(*) AS ZeroCount
FROM Sales s1
INNER JOIN Sales s2
ON s2.ProdID = s1.ProdID
AND s2.WeekEnd >= s1.WeekEnd
AND s2.WeekEnd <= DATEADD(WEEK, #NumWeeks + 1, s1.WeekEnd)
WHERE s1.Sales > 0
GROUP BY s1.ProdID, s1.WeekEnd
HAVING COUNT(*) >= #NumWeeks
Now, this is making a critical assumption, namely that there are no duplicate entries (only 1 per product per week) and that new data is actually entered every week. With these assumptions taken into account, if we look at the 27 weeks after a non-zero sales week and find that there were 26 total weeks with zero sales, then we can deduce logically that they had to be 26 consecutive weeks.
Note that this will ignore products that had zero sales from the start; there has to be a non-zero week to anchor it. If you want to include products that had no sales since the beginning, then add the following line after `WHERE s1.Sales > 0':
OR s1.WeekEnd = (SELECT MIN(WeekEnd) FROM Sales WHERE ProdID = s1.ProdID)
This will slow the query down a lot but guarantees that the first week of "recorded" sales will always be taken into account.
SELECT DISTINCT
s1.ProdId
FROM (
SELECT
ProdId,
ROW_NUMBER() OVER (PARTITION BY ProdId ORDER BY WeekEnd) AS rownum,
WeekEnd
FROM Sales
WHERE Sales <> 0
) s1
INNER JOIN (
SELECT
ProdId,
ROW_NUMBER() OVER (PARTITION BY ProdId ORDER BY WeekEnd) AS rownum,
WeekEnd
FROM Sales
WHERE Sales <> 0
) s2
ON s1.ProdId = s2.ProdId
AND s1.rownum + 1 = s2.rownum
AND DateAdd(WEEK, 26, s1.WeekEnd) = s2.WeekEnd;