Group/Summarize each row with removal row if total quantity is 0 - sql

How to group/summarize as example image below.
The same data will be grouped based on Date and Item columns.
The quantity will be sum.
If the negative quantity is more than total quantity of the min date (total qty = 0), that row will be removed.
This condition will continue for the next min date as well.
In this case 1-Jan-2020 and 2-Jan-2020 will be removed because it negative quantity is more than total of those 2 days.
In case you want sample table, please use script below.
CREATE TABLE #temp_table(
[id] [int] IDENTITY(1,1) NOT NULL,
[trans_date] [date] NOT NULL,
[item] [nvarchar](40) NOT NULL,
[qty] [int] NOT NULL,
)
INSERT INTO #temp_table ( trans_date, item, qty )
VALUES
( '1-Jan-2020', 'Item A', 2 )
INSERT INTO #temp_table ( trans_date, item, qty )
VALUES
( '2-Jan-2020', 'Item A', 4 )
INSERT INTO #temp_table ( trans_date, item, qty )
VALUES
( '3-Jan-2020', 'Item B', 1 )
INSERT INTO #temp_table ( trans_date, item, qty )
VALUES
( '3-Jan-2020', 'Item A', 3 )
INSERT INTO #temp_table ( trans_date, item, qty )
VALUES
( '4-Jan-2020', 'Item A', -1 )
INSERT INTO #temp_table ( trans_date, item, qty )
VALUES
( '5-Jan-2020', 'Item A', -6 )
INSERT INTO #temp_table ( trans_date, item, qty )
VALUES
( '6-Jan-2020', 'Item A', 4 )
SELECT * FROM #temp_table
DROP TABLE #temp_table
My 1st attempt was
select
trans_date
, item
, SUM(qty)
from temp_table
group BY
trans_date
, item
My 2nd attempt, this attempt is feel like I'm lacking of some condition to reduce the next row when I first row is 0.
select
temp_table.trans_date
, temp_table.item
, SUM(temp_table.qty) + SUM(neg_table.neg_qty)
from temp_table
OUTER APPLY (
SELECT ISNULL( SUM(neg.qty), 0) AS neg_qty FROM pca_temp_table neg
WHERE 1=1
and temp_table.item = neg.item
and neg.qty < 0
) as neg_table
WHERE qty > 0
group BY
trans_date
, item

Hope this query works fine for you:
select MAX(CASE WHEN Quantity>0 THEN DATE ELSE NULL END), ITEM, SUM(Quantity)
from #T
group BY ITEM

Related

Combining rows and columns in SQL

I am trying to convert my rows into columns, or my columns into rows... I am a little confused with which it is exactly but here's what I would want it to look like
Original table:
Month | Price
1 500
2 600
3 700
what it needs to look like:
1 2 3
500 600 700
Could anyone tell me how his could be done?
EDIT:
CREATE table #yourtable (
[Id] int,
[Value] varchar(6),
[ColumnName] varchar(13)) ;
INSERT INTO #yourtable (
[Id],
[Value],
[ColumnName])
VALUES
(1, '1', 'Month'),
(2, '500', 'Price') ;
select
Month,
Price
from (
select
value,
columnname
from #yourtable ) d
pivot
(max(value) for columnname in (Month, Price) ) piv;
You wrote an almost correct query.
select
Month,
Price
from (
select
value,
columnname
from #yourtable) d
pivot
(max(value) for columnname in ('Month' AS Month, 'Price' AS Price) ) piv;

SQL Grand Total row from Multi table Select Query

I have been able to use the below SQL query to give me 4 rows of data
SELECT 'Sales Order ' as Type, Format(Sum(T1.C_NetAmountLessDiscount),
'#.00') As NetAmount, Format(Sum(T1.C_MarginAmount), '#.00') As
MarginAmount, Format(Sum(T1.C_MarginAmount)/Sum(T1.C_NetAmountLessDiscount),
'#.00%') As Margin
From T_SalesOrder as T1
Where cast(T1.C_Date as Date) = cast(getdate() as Date) AND T1.C_OrderType
!= 'BK' AND T1.C_OrderType != 'INV'
Union
SELECT 'Despatch Notes ' AS Type, Format(Sum(T1.C_NetAmountLessDiscount),
'#.00') As NetAmount, Format(Sum(T1.C_MarginAmount), '#.00') As
MarginAmount, Format(Sum(T1.C_MarginAmount)/Sum(T1.C_NetAmountLessDiscount),
'#.00%') As Margin
From T_SalesDeliveryNote as T1
Where cast(T1.C_Date as Date) = cast(getdate() as Date) AND T1.C_Order is
null AND T1.C_BillingStatus = '0'
Union
SELECT 'Invoices ' AS Type, Format(Sum(T1.C_NetAmountLessDiscount), '#.00')
As NetAmount, Format(Sum(T1.C_MarginAmount), '#.00') As MarginAmount,
Format(Sum(T1.C_MarginAmount)/Sum(T1.C_NetAmountLessDiscount), '#.00%') As
Margin
From T_SalesInvoice as T1
Where cast(T1.C_Date as Date) = cast(getdate() as Date) And
T1.C_DeliveryNote is null And T1.C_SourceOrder is null and T1.C_InvoiceType
= '0'
Union
SELECT 'Credit Notes ' AS Type, Format(Sum(T1.C_NetAmountLessDiscount), '-
#.00') As NetAmount, Format(Sum(T1.C_MarginAmount), '-#.00') As
MarginAmount, Format(Sum(T1.C_MarginAmount)/Sum(T1.C_NetAmountLessDiscount),
'#.00%') As Margin
From T_SalesCreditNote as T1
Where cast(T1.C_Date as Date) = cast(getdate() as Date)
This gives me a breakdown of the orders as I need but I also want to have a Grand Total row that sums each column.
If I insert the above sql query into the below
Select 'Grand Total', Sum(CAST(NetAmount AS float)), Sum(CAST(MarginAmount
AS float)),null
From
(
-----Above SQL Query in Here
)tbl
I get one single line with the correct totals but no breakdown rows.
How can I do this so it displays the four rows of each type and a grand total row at the bottom.
Use common table expressions (CTE).
WITH t AS(
-- Your query goes here
-- SELECT 1 AS A, 'Sales Order' ...
-- UNION SELECT 2 AS A, 'Despatch Notes' ...
-- UNION SELECT 3 AS A, 'Invoices' ...
-- UNION SELECT 4 AS A, 'Credit Notes' ...
)
SELECT
A,
Type,
NetAmount,
MarginAmount,
Margin
FROM
t
UNION SELECT
5 AS A,
'Grand Total' AS Type,
FORMAT(SUM(CAST(NetAmount AS FLOAT)), '#.00') As NetAmount,
FORMAT(SUM(CAST(MarginAmount AS FLOAT)), '#.00') As MarginAmount,
NULL
FROM
t
ORDER BY
A
Note: I added formatting the total values to ensure the total values of NetAmount and MarginAmount to share the same data type as the breakdown values. Consider to remove formatting the values from your query and add it to the presentation layer of your application.
Grouping with a ROLLUP might be just the trick for this.
Simplified Example Snippet:
DECLARE #Table1 TABLE (
ID INT IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(30) NOT NULL,
NetAmount DECIMAL(10,2) NOT NULL DEFAULT 0
);
DECLARE #Table2 TABLE (
ID INT IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(30) NOT NULL,
NetAmount DECIMAL(10,2) NOT NULL DEFAULT 0
);
INSERT INTO #Table1 (Name, NetAmount)
VALUES ('A', 4.1), ('A', 6.1);
INSERT INTO #Table2 (Name, NetAmount)
VALUES ('B', 9.1), ('B', 11.1);
WITH CTE_AMOUNTS AS
(
SELECT 'T1' as [Type],
SUM(NetAmount) AS [NetAmount]
FROM #Table1
UNION ALL
SELECT 'T2', SUM(NetAmount)
FROM #Table2
)
SELECT
COALESCE([Type], 'Grand Total') AS [Type],
SUM([NetAmount]) AS [NetAmount]
FROM CTE_AMOUNTS
GROUP BY Type WITH ROLLUP
ORDER BY GROUPING_ID(Type), Type;
Returns:
Type NetAmount
T1 10,20
T2 20,20
Grand Total 30,40

SQL number greater than select results

I'm struggling to think of a way to do this with T-SQL.
I have a table which is populated every 5 seconds with the prices of three currencies (GBP, EUR & USD)
I've created a trigger (after insert), which selects the last 5 records entered for a given currency:
SELECT TOP 5 Price from dbo.prices where coin='GBP' ORDER BY Date Desc
I want to determine if the last inserted currency price is greater than the selected 5 above, how do i do this?
Thanks
As I guess: there cant be two entries for the same currency at one time. Only one insert per currency per some time (5sec). So this should fit yours requirements:
declare #prices table ([Date] int IDENTITY(1,1) primary key, Price float, coin varchar(3));
insert into #prices (coin, Price) values
('GBP', 3.20),('EUR', 3.14),('USD', 3.14),
('GBP', 3.17),('EUR', 3.16),('USD', 3.11),
('GBP', 3.14),('EUR', 3.13),('USD', 3.16),
('GBP', 3.15),('EUR', 3.12),('USD', 3.17),
('GBP', 3.16),('EUR', 3.17),('USD', 3.11),
('GBP', 3.15),('EUR', 3.14),('USD', 3.12),
('GBP', 3.19),('EUR', 3.14),('USD', 3.16)
select
case
when NEW.Price > PREV.Price Then 'yes'
else 'No'
end as CURR_JUMP_UP
from
(
select top 1 COALESCE(Price,0) Price, [Date]
from #prices where coin='GBP' order by [Date] desc
) NEW
cross apply
(
select MAX(Price) Price from
(
select top 5 Price
from #prices
where coin='GBP' and [Date]<NEW.[Date]
order by [Date] desc
) t
) PREV
Try this query:
DECLARE #AmountLastFiveEntry DECIMAL= (SELECT TOP 5 SUM(Price) FROM dbo.prices WHERE
ID NOT IN (SELECT TOP 1 ID
FROM dbo.prices where coin='GBP' ORDER BY Date Desc) where coin='GBP' ORDER BY Date Desc)
IF #AmountLastFiveEntry<(SELECT TOP 1 Price
FROM dbo.prices where coin='GBP' ORDER BY Date Desc)
BEGIN
SELECT #AmountLastFiveEntry --To do task
END
Trigger part is confusing
This will report if the latest price is higher (or equal) to the largest of the prior 5.
declare #currency table (iden int IDENTITY(1,1) primary key, exchange smallint, coin tinyint);
insert into #currency (coin, exchange) values
(1, 1)
, (1, 2)
, (1, 3)
, (1, 4)
, (1, 5)
, (1, 6)
, (2, 1)
, (2, 2)
, (2, 3)
, (2, 4)
, (2, 5)
, (2, 3);
select cccc.coin, cccc.exchange
, case when cccc.rn = cccc.rne then 'yes'
else 'no'
end as 'high'
from ( select ccc.iden, ccc.coin, ccc.exchange, ccc.rn
, ROW_NUMBER() over (partition by ccc.coin order by ccc.exchange desc, ccc.rn) rne
from ( select cc.iden, cc.coin, cc.exchange, cc.rn
from ( select c.iden, c.coin, c.exchange
, ROW_NUMBER() over (partition by coin order by iden desc) as rn
from #currency c
) cc
where cc.rn <= 6
) ccc
) cccc
where cccc.rn = 1
order by cccc.coin

SQL query when result is empty

I have a table like this
USER itemnumber datebought (YYYYmmDD)
a 1 20160101
b 2 20160202
c 3 20160903
d 4 20160101
Now I have to show the total number of items bought by each user after date 20160202 (2 february 2016)
I used
SELECT USER, COUNT(itemnumber)<br/>
FROM TABLE<br/>
WHERE datebought >= 20160202<br/>
GROUP BY USER<br>
It gives me results
b 1
c 1
but I want like this
a 0
b 1
c 1
d 0
Please tell me what is the most quick method / efficient method to do that ?
Try like this,
DECLARE #table TABLE
(
[USER] VARCHAR(1),
itemnumber INT,
datebought DATE
)
INSERT INTO #TABLE VALUES
('a',1,'20160101'),
('b',2,'20160202'),
('b',2,'20160202'),
('b',2,'20160202'),
('c',3,'20160903'),
('d',4,'20160101')
SELECT *
FROM #TABLE
SELECT [USER],
Sum(CASE
WHEN datebought >= '20160202' THEN 1
ELSE 0
END) AS ITEMCOUNT
FROM #TABLE
GROUP BY [USER]
Use this
SELECT USER, COUNT(itemnumber)
FROM TABLE
WHERE datebought >= 20160202
GROUP BY USER
Though this query won't be a good idea for the large amount of data:
SELECT USER, COUNT(itemnumber)
FROM TABLE
WHERE datebought >= 20160202
GROUP BY USER
UNION
SELECT DISTINCT USER, 0
FROM TABLE
WHERE datebought < 20160202
USE tempdb
GO
DROP TABLE test1
CREATE TABLE test1(a NVARCHAR(10), ino INT, datebought INT)
INSERT INTO dbo.test1
( a, ino, datebought )
VALUES ( 'a' , 1 , 20160101)
INSERT INTO dbo.test1
( a, ino, datebought )
VALUES ( 'b' , 2 , 20160202)
INSERT INTO dbo.test1
( a, ino, datebought )
VALUES ( 'c' , 3 , 20160903)
INSERT INTO dbo.test1
( a, ino, datebought )
VALUES ( 'd' , 4 , 20160101)
SELECT * FROM dbo.test1
SELECT a, COUNT(ino) OVER(PARTITION BY a) FROM dbo.test1
WHERE datebought>=20160202
UNION ALL
SELECT a, 0 FROM dbo.test1
WHERE datebought<20160202
ORDER BY a

Sum when field does not exist

I have the following table:
create table #tbl
(
[type] varchar(20),
[qty] int
)
insert into #tbl values ('Type A', 10)
insert into #tbl values ('Type A', 15)
insert into #tbl values ('Type B', 5)
insert into #tbl values ('Type B', 8)
Now I want to display the total qty of each individual 'type':
select
isnull([type], 'Other') as [type],
sum(case
when [type] = 'Type A' then qty
when [type] = 'Type B' then qty
when [type] = 'Type C' then qty
else 0
end) as [total]
from #tbl
where [type] in ('Type A', 'Type B', 'Type C')
group by [type]
It correctly sums up each 'type'. Here's the result:
type total
--------------
Type A 25
Type B 13
But I want Type C to be included in the result as well (with a total qty of 0).
type total
--------------
Type A 25
Type B 13
Type C 0
How can I accomplish that?
I'm using MS SQL Server 2005.
The problem is that you don't have Type C in the table so there is nothing to return. One way you could this is to create a derived table with all of the values that you want include and then LEFT JOIN your table:
select d.type,
sum(coalesce(t.qty, 0)) Total
from
(
select 'Type A' type union all
select 'Type B' type union all
select 'Type C' type
) d
left join tbl t
on d.type = t.type
group by d.type;
See SQL Fiddle with Demo
You will need a table containing the list of types you want to report and do a left join on that. Something like the following:
create table #tbl
(
[type] varchar(20),
[qty] int
);
insert into #tbl values ('Type A', 10)
insert into #tbl values ('Type A', 15)
insert into #tbl values ('Type B', 5)
insert into #tbl values ('Type B', 8)
create table #types ( [type] varchar(20) );
insert into #types values ('Type A' );
insert into #types values ('Type B' );
insert into #types values ('Type C' );
select t.[type], [Total] = IsNull(t.[total], 0)
from ( select [type] = IsNull(t.[Type], 'Other')
, [total] = sum(tbl.[qty])
from #types t
left
join #tbl tbl ON tbl.[type] = t.type
group
by t.[type]
) as t
;
The sub-query is necessary to convert the NULL sums to zero.
Also you can get the result by applying both the UNPIVOT and the PIVOT operators.
SELECT type, qty
FROM(
SELECT COALESCE([Type A], 0) AS [Type A],
COALESCE([Type B], 0) AS [Type B],
COALESCE([Type C], 0) AS [Type C]
FROM (
SELECT [type], [qty]
FROM #tbl
) x
PIVOT (
SUM([qty]) FOR [type] IN([Type A], [Type B], [Type C])
) p
)x
UNPIVOT (
[qty] FOR [type] IN([Type A], [Type B], [Type C])
) u
Demo on SQLFiddle