What is wrong with my SQL query SUM group-by - sql

Hello i have a sql query and it does not count my one row. which is called Spend, you can see it in the fiddle. what is wrong with my code?
I just need basic table
Month ID GOT SPEND
1 1 100 50
2 1 500 200
1 2 200 50
I have created the fiddle http://sqlfiddle.com/#!9/3623b1/2
Could you please help me?
Here is the query:
select
keliones_lapas.Vairuot_Id,
MONTH(keliones_lapas.Data_darbo),
sum(keliones_lapas.uzdarbis) as Got,
coalesce(Suma, 0) AS Spend,
(sum(keliones_lapas.uzdarbis) - coalesce(Suma, 0)) Total
from keliones_lapas
left join (
select Vairuotas,
MONTH(Data_islaidu) as Data_islaidu,
sum(Suma) as Suma
from islaidos
group by Vairuotas, MONTH(Data_islaidu)) islaidos
on keliones_lapas.Vairuot_Id = islaidos.Vairuotas
and MONTH(keliones_lapas.Data_darbo) = MONTH(islaidos.Data_islaidu)
group by keliones_lapas.Vairuot_Id, MONTH(keliones_lapas.Data_darbo), Suma
order by keliones_lapas.Vairuot_Id, MONTH(keliones_lapas.Data_darbo);

TRY THIS: You are taking already month in your subquery then again using MONTH to retrieve from month in the join so it's returning NULL and not matching with any month of keliones_lapas
SELECT
keliones_lapas.Vairuot_Id,
MONTH(keliones_lapas.Data_darbo),
SUM(keliones_lapas.uzdarbis) AS Got,
COALESCE(Suma, 0) AS Spend,
(SUM(keliones_lapas.uzdarbis) - COALESCE(Suma, 0)) Total
FROM keliones_lapas
LEFT JOIN (
SELECT Vairuotas,
MONTH(Data_islaidu) AS Data_islaidu, --It's already in MONTH
SUM(Suma) AS Suma
FROM islaidos
GROUP BY Vairuotas, MONTH(Data_islaidu)) islaidos
ON keliones_lapas.Vairuot_Id = islaidos.Vairuotas
AND MONTH(keliones_lapas.Data_darbo) = Data_islaidu --No need to use MONTH or `vice versa`
GROUP BY keliones_lapas.Vairuot_Id, MONTH(keliones_lapas.Data_darbo), Suma
ORDER BY keliones_lapas.Vairuot_Id, MONTH(keliones_lapas.Data_darbo)

Related

SQL sum with limit

i need to sum working hours from employees with some limitations. My code right now just for one employee:
SELECT rp.mnr,
trunc(sum(rp.OUT_DATE-rp.IN_DATE)*24, 2) AS "Hours"
FROM table1 rp
WHERE rp.CALC_DATE >= '01.04.2022' and rp.CALC_DATE <= '30.4.2022'
AND rp.MNR = 90590
GROUP BY rp.MNR
ORDER BY rp.mnr desc
MNR |Hours |
-----+------+
90590|181.98|
I need to split this sum with hours limit 50.. Result should look like:
MNR |Hours |
-----+------+
90590|50.00|
90590|50.00|
90590|50.00|
90590|31.98|
Anyone can help me with that ?
DB: Oracle.
Thank you!
MF
You have to generate the required rows to get your desired output. CONNECT BY clause is one of the method -
WITH DATA AS (<YOUR SELECT QUERY GIVING COLUMNS AS MNR AND HOURS>),
FIXED_AMOUNT AS (SELECT 50 AMT FROM DUAL),
CALC AS (SELECT D.*, AMT, SUM(AMT) OVER (ORDER BY ROWNUM) CUMM_SUM
FROM DATA D, FIXED_AMOUNT
CONNECT BY (LEVEL - 1) * 50 <= HOURS)
SELECT MNR, CASE WHEN HOURS_ - CUMM_SUM < 0
THEN HOURS_ - CUMM_SUM + AMT
ELSE AMT
END HOURS
FROM CALC;
Demo.

SQL - Calculate the difference in number of orders by month

I am working on the orders table provided by this site, it has its own editor where you can test your SQL statements.
The order table looks like this
order_id
customer_id
order_date
1
7000
2016/04/18
2
5000
2016/04/18
3
8000
2016/04/19
4
4000
2016/04/20
5
NULL
2016/05/01
I want to get the difference in the number of orders for subsequent months.
To elaborate, the number of orders each month would be like this
SQL Statement
SELECT
MONTH(order_date) AS Month,
COUNT(MONTH(order_date)) AS Total_Orders
FROM
orders
GROUP BY
MONTH(order_date)
Result:
Month
Total_Orders
4
4
5
1
Now my goal is to get the difference in subsequent months which would be
Month
Total_Orders
Total_Orders_Diff
4
4
4 - Null = Null
5
1
1 - 4 = -3
My strategy was to self-join following this answer
This was my attempt
SELECT
MONTH(a.order_date),
COUNT(MONTH(a.order_date)),
COUNT(MONTH(b.order_date)) - COUNT(MONTH(a.order_date)) AS prev,
MONTH(b.order_date)
FROM
orders a
LEFT JOIN
orders b ON MONTH(a.order_date) = MONTH(b.order_date) - 1
GROUP BY
MONTH(a.order_date)
However, the result was just zeros (as shown below) which suggests that I am just subtracting from the same value rather than from the previous month (or subtracting from a null value)
MONTH(a.order_date)
COUNT(MONTH(a.order_date))
prev
MONTH(b.order_date)
4
4
0
NULL
5
1
0
NULL
Do you have any suggestions as to what I am doing wrong?
You have to use LAG window function in your SELECT statement.
LAG provides access to a row at a given physical offset that comes
before the current row.
So, this is what you need:
SELECT
MONTH(order_date) as Month,
COUNT(MONTH(order_date)) as Total_Orders,
COUNT(MONTH(order_date)) - (LAG (COUNT(MONTH(order_date))) OVER (ORDER BY (SELECT NULL))) AS Total_Orders_Diff
FROM orders
GROUP BY MONTH(order_date);
Here in an example on the SQL Fiddle: http://sqlfiddle.com/#!18/5ed75/1
Solution without using LAG window function:
WITH InitCTE AS
(
SELECT MONTH(order_date) AS Month,
COUNT(MONTH(order_date)) AS Total_Orders
FROM orders
GROUP BY MONTH(order_date)
)
SELECT InitCTE.Month, InitCTE.Total_Orders, R.Total_Orders_Diff
FROM InitCTE
OUTER APPLY (SELECT TOP 1 InitCTE.Total_Orders - CompareCTE.Total_Orders AS Total_Orders_Diff
FROM InitCTE AS CompareCTE
WHERE CompareCTE.Month < InitCTE.Month) R;
Something like the following should give you what you want - disclaimer, untested!
select *, Total_Orders - lag(Total_orders,1) over(order by Month) as Total_Orders_Diff
from (
select Month(order_date) as Month, Count(*) as Total_Orders
From orders
Group by Month(order_date)
)o

Grouping multiple selects within a SQL query

I have a table Supplier with two columns, TotalStock and Date. I'm trying to write a single query that will give me stock totals by week / month / year for a list of suppliers.
So results will look like this..
SUPPLIER WEEK MONTH YEAR
SupplierA 50 100 2000
SupplierB 60 150 2500
SupplierC 15 25 200
So far I've been playing around with multiple selects but I can't get any further than this:
SELECT Supplier,
(
SELECT Sum(TotalStock)
FROM StockBreakdown
WHERE Date >= '2014-5-12'
GROUP BY Supplier
) AS StockThisWeek,
(
SELECT Sum(TotalStock)
FROM StockBreakdown
WHERE Date >= '2014-5-1'
GROUP BY Supplier
) AS StockThisMonth,
(
SELECT Sum(TotalStock)
FROM StockBreakdown
WHERE Date >= '2014-1-1'
GROUP BY Supplier
) AS StockThisYear
This query throws an error as each individual grouping returns multiple results. I feel that I'm close to the solution but can't work out where to go
You don't have to use subqueries to achieve what you want :
SELECT Supplier
, SUM(CASE WHEN Date >= CAST('2014-05-12' as DATE) THEN TotalStock END) AS StockThisWeek
, SUM(CASE WHEN Date >= CAST('2014-05-01' as DATE) THEN TotalStock END) AS StockThisMonth
, SUM(CASE WHEN Date >= CAST('2014-01-01' as DATE) THEN TotalStock END) AS StockThisYear
FROM StockBreakdown
GROUP BY Supplier
You may need to make the selects for the columns return only a single result. You could try this (not tested currently):
SELECT Supplier,
(
SELECT TOP 1 StockThisWeek FROM
(
SELECT Supplier, Sum(TotalStock) AS StockThisWeek
FROM StockBreakdown
WHERE Date >= '2014-5-12'
GROUP BY Supplier
) tmp1
WHERE tmp1.Supplier = Supplier
) AS StockThisWeek,
(
SELECT TOP 1 StockThisMonth FROM
(
SELECT Supplier, Sum(TotalStock) AS StockThisMonth
FROM StockBreakdown
WHERE Date >= '2014-5-1'
GROUP BY Supplier
) tmp2
WHERE tmp2.Supplier = Supplier
) AS StockThisMonth,
...
This selects the supplier and then tries to create two columns StockThisWeek and StockThisMonth by selecting the first entry from the select you created before. As through the GROUP BY there should only be one entry per supplier, so you don't lose and data.

Using a column in sql join without adding it to group by clause

My actual table structures are much more complex but following are two simplified table definitions:
Table invoice
CREATE TABLE invoice (
id integer NOT NULL,
create_datetime timestamp with time zone NOT NULL,
total numeric(22,10) NOT NULL
);
id create_datetime total
----------------------------
100 2014-05-08 1000
Table payment_invoice
CREATE TABLE payment_invoice (
invoice_id integer,
amount numeric(22,10)
);
invoice_id amount
-------------------
100 100
100 200
100 150
I want to select the data by joining above 2 tables and selected data should look like:-
month total_invoice_count outstanding_balance
05/2014 1 550
The query I am using:
select
to_char(date_trunc('month', i.create_datetime), 'MM/YYYY') as month,
count(i.id) as total_invoice_count,
(sum(i.total) - sum(pi.amount)) as outstanding_balance
from invoice i
join payment_invoice pi on i.id=pi.invoice_id
group by date_trunc('month', i.create_datetime)
order by date_trunc('month', i.create_datetime);
Above query is giving me incorrect results as sum(i.total) - sum(pi.amount) returns (1000 + 1000 + 1000) - (100 + 200 + 150) = 2550.
I want it to return (1000) - (100 + 200 + 150) = 550
And I cannot change it to i.total - sum(pi.amount), because then I am forced to add i.total column to group by clause and that I don't want to do.
You need a single row per invoice, so aggregate payment_invoice first - best before you join.
When the whole table is selected, it's typically fastest to aggregate first and join later:
SELECT to_char(date_trunc('month', i.create_datetime), 'MM/YYYY') AS month
, count(*) AS total_invoice_count
, (sum(i.total) - COALESCE(sum(pi.paid), 0)) AS outstanding_balance
FROM invoice i
LEFT JOIN (
SELECT invoice_id AS id, sum(amount) AS paid
FROM payment_invoice pi
GROUP BY 1
) pi USING (id)
GROUP BY date_trunc('month', i.create_datetime)
ORDER BY date_trunc('month', i.create_datetime);
LEFT JOIN is essential here. You do not want to loose invoices that have no corresponding rows in payment_invoice (yet), which would happen with a plain JOIN.
Accordingly, use COALESCE() for the sum of payments, which might be NULL.
SQL Fiddle with improved test case.
Do the aggregation in two steps. First aggregate to a single line per invoice, then to a single line per month:
select
to_char(date_trunc('month', t.create_datetime), 'MM/YYYY') as month,
count(*) as total_invoice_count,
(sum(t.total) - sum(t.amount)) as outstanding_balance
from (
select i.create_datetime, i.total, sum(pi.amount) amount
from invoice i
join payment_invoice pi on i.id=pi.invoice_id
group by i.id, i.total
) t
group by date_trunc('month', t.create_datetime)
order by date_trunc('month', t.create_datetime);
See sqlFiddle
SELECT TO_CHAR(invoice.create_datetime, 'MM/YYYY') as month,
COUNT(invoice.create_datetime) as total_invoice_count,
invoice.total - payments.sum_amount as outstanding_balance
FROM invoice
JOIN
(
SELECT invoice_id, SUM(amount) AS sum_amount
FROM payment_invoice
GROUP BY invoice_id
) payments
ON invoice.id = payments.invoice_id
GROUP BY TO_CHAR(invoice.create_datetime, 'MM/YYYY'),
invoice.total - payments.sum_amount

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;