How to declare an array inside MS SQL Server Stored Procedure? - sql-server-2008

I need to declare 12 decimal variables, corresponding to each month's year, with a cursor I sum values to this variables, then later I Update some sales information.
I don't know if sql server has this syntax
Declare MonthsSale(1 to 12) as decimal(18,2)
This code works Ok. !
CREATE PROCEDURE [dbo].[proc_test]
AS
BEGIN
--SET NOCOUNT ON;
DECLARE #monthsales TABLE ( monthnr int, amount decimal(18,2) )
-- PUT YOUR OWN CODE HERE
-- THIS IS TEST CODE
-- 1 REPRESENTS JANUARY, ...
INSERT #monthsales (monthnr, amount) VALUES (1, 100)
INSERT #monthsales (monthnr, amount) VALUES (1, 100)
INSERT #monthsales (monthnr, amount) VALUES (2, 200)
INSERT #monthsales (monthnr, amount) VALUES (3, 300)
INSERT #monthsales (monthnr, amount) VALUES (4, 400)
INSERT #monthsales (monthnr, amount) VALUES (5, 500)
INSERT #monthsales (monthnr, amount) VALUES (6, 600)
INSERT #monthsales (monthnr, amount) VALUES (7, 700)
INSERT #monthsales (monthnr, amount) VALUES (8, 800)
INSERT #monthsales (monthnr, amount) VALUES (9, 900)
INSERT #monthsales (monthnr, amount) VALUES (10, 1000)
INSERT #monthsales (monthnr, amount) VALUES (11, 1100)
INSERT #monthsales (monthnr, amount) VALUES (12, 1200)
SELECT monthnr, SUM(amount) AS SUM_MONTH_1 FROM #monthsales WHERE monthnr = 1 GROUP BY monthnr
SELECT monthnr, SUM(amount) AS SUM_MONTH_2 FROM #monthsales WHERE monthnr = 2 GROUP BY monthnr
SELECT monthnr, SUM(amount) AS SUM_MONTH_3 FROM #monthsales WHERE monthnr = 3 GROUP BY monthnr
SELECT monthnr, SUM(amount) AS SUM_MONTH_4 FROM #monthsales WHERE monthnr = 4 GROUP BY monthnr
SELECT monthnr, SUM(amount) AS SUM_MONTH_5 FROM #monthsales WHERE monthnr = 5 GROUP BY monthnr
SELECT monthnr, SUM(amount) AS SUM_MONTH_6 FROM #monthsales WHERE monthnr = 6 GROUP BY monthnr
SELECT monthnr, SUM(amount) AS SUM_MONTH_7 FROM #monthsales WHERE monthnr = 7 GROUP BY monthnr
SELECT monthnr, SUM(amount) AS SUM_MONTH_8 FROM #monthsales WHERE monthnr = 8 GROUP BY monthnr
SELECT monthnr, SUM(amount) AS SUM_MONTH_9 FROM #monthsales WHERE monthnr = 9 GROUP BY monthnr
SELECT monthnr, SUM(amount) AS SUM_MONTH_10 FROM #monthsales WHERE monthnr = 10 GROUP BY monthnr
SELECT monthnr, SUM(amount) AS SUM_MONTH_11 FROM #monthsales WHERE monthnr = 11 GROUP BY monthnr
SELECT monthnr, SUM(amount) AS SUM_MONTH_12 FROM #monthsales WHERE monthnr = 12 GROUP BY monthnr
-- END TEST CODE
END

You could declare a table variable (Declaring a variable of type table):
declare #MonthsSale table(monthnr int)
insert into #MonthsSale (monthnr) values (1)
insert into #MonthsSale (monthnr) values (2)
....
You can add extra columns as you like:
declare #MonthsSale table(monthnr int, totalsales tinyint)
You can update the table variable like any other table:
update m
set m.TotalSales = sum(s.SalesValue)
from #MonthsSale m
left join Sales s on month(s.SalesDt) = m.MonthNr

Is there a reason why you aren't using a table variable and the aggregate SUM operator, instead of a cursor? SQL excels at set-oriented operations. 99.87% of the time that you find yourself using a cursor, there's a set-oriented alternative that's more efficient:
declare #MonthsSale table
(
MonthNumber int,
MonthName varchar(9),
MonthSale decimal(18,2)
)
insert into #MonthsSale
select
1, 'January', 100.00
union select
2, 'February', 200.00
union select
3, 'March', 300.00
union select
4, 'April', 400.00
union select
5, 'May', 500.00
union select
6, 'June', 600.00
union select
7, 'July', 700.00
union select
8, 'August', 800.00
union select
9, 'September', 900.00
union select
10, 'October', 1000.00
union select
11, 'November', 1100.00
union select
12, 'December', 1200.00
select * from #MonthsSale
select SUM(MonthSale) as [TotalSales] from #MonthsSale

T-SQL doesn't support arrays that I'm aware of.
What's your table structure? You could probably design a query that does this instead:
select
month,
sum(sales)
from sales_table
group by month
order by month

Great question and great idea, but in SQL you'll need to do this:
For data type datetime, something like this-
declare #BeginDate datetime = '1/1/2016',
#EndDate datetime = '12/1/2016'
create table #months (dates datetime)
declare #var datetime = #BeginDate
while #var < dateadd(MONTH, +1, #EndDate)
Begin
insert into #months Values(#var)
set #var = Dateadd(MONTH, +1, #var)
end
If all you really want is numbers, do this-
create table #numbas (digit int)
declare #var int = 1 --your starting digit
while #var <= 12 --your ending digit
begin
insert into #numbas Values(#var)
set #var = #var +1
end

You can declare an array using the VALUES keyword. Your example can be expressed succintly in the following form:
SELECT * FROM (VALUES
(1,100),
(2,200),
(3,300),
(4,400),
(5,500),
(6,600),
(7,700),
(8,800),
(9,900),
(10,1000),
(11,1100),
(12,1200)
) MonthSale (monthnr, Amount)
Feel free to INSERT it into a table or place a WHERE or GROUP condition for your purposes.

Related

Select a row from similar rows MYSQL

I have a Table like this
CREATE TABLE prova
(`ID` int, `CODCLI` longtext, `RIFYEAR` int, `VAL` int)
;
INSERT INTO prova
(`ID`, `CODCLI`, `RIFYEAR`, `VAL`)
VALUES
(1, '1dad000', 2020, 150),
(2, '500', 2020, 100),
(3, '1dad000', 2021, 50),
(4, '1dad000', 2022, 70),
(5, '2000', 2023, 80)
;
http://www.sqlfiddle.com/#!9/7697a4/4
and i want to select these rows
2, '500', 2020, 100
3, '1dad000', 2021, 50
4, '1dad000', 2022, 70
how can i do?
i wrote something like this
SELECT *
FROM prova
WHERE CODCLI IN ('500','1dad000')
but when 'RIFYEAR' is same, i want to select only the row that has CODCLI = 500.
thanks for your help ;)
One method uses window function:
SELECT p.*
FROM (SELECT p.*,
ROW_NUMBER() OVER (PARTITION BY year ORDER BY CODCLI DESC) as seqnum
FROM prova p
WHERE CODCLI IN ('500', '1dad000')
) p
WHERE seqnum = 1;
This is guaranteed to return one row per year.
Or using NOT EXISTS:
select p.*
from prova p
where p.codcli = '500' or
(p.codcli = '1dad000' and
not exists (select 1
from prova p2
where p2.year = p.year and p2.codcli = '500'
)
);
This can return duplicates per year if there are duplicates in the prova.

MySQL how to sum amount of excessive transactions in any 15 minute window?

MySQL
create table tran(
id int,
tran_dt datetime,
card_id int,
merchant_id int,
amount int
);
#Customer #1
insert into tran values(11, '2015-01-01 01:59:00', 1, 1, 2);
insert into tran values(12, '2015-01-01 02:01:00', 1, 1, 4);
insert into tran values(13, '2015-01-01 02:02:00', 1, 1, 6);
#Customer #2
insert into tran values(21, '2015-01-01 01:00:00', 2, 1, 10);
insert into tran values(22, '2015-01-01 01:01:00', 2, 1, 20);
insert into tran values(23, '2015-01-01 01:02:00', 2, 1, 30);
insert into tran values(24, '2015-01-01 01:03:00', 2, 1, 20);
insert into tran values(29, '2015-01-02 01:03:00', 2, 1, 10);
#Customer #3
insert into tran values(31, '2015-01-01 01:00:00', 3, 1, 100);
insert into tran values(32, '2015-01-01 01:00:00', 3, 1, 200);
insert into tran values(33, '2015-01-01 01:00:00', 3, 1, 100);
insert into tran values(34, '2015-01-01 01:00:00', 3, 1, 200);
insert into tran values(35, '2015-01-01 01:00:00', 3, 1, 100);
A report of which cards have been used 3+ times in any 15 minute window at the same merchant:
SELECT t1.card_id, t1.merchant_id, count(distinct t1.id)+1 as Count, sum(t1.amount) 'SumAmount'
FROM tran t1
INNER JOIN tran t2
on t2.card_id=t1.card_id
and t2.merchant_id=t1.merchant_id
and t2.tran_dt <= DATE_ADD(t1.tran_dt, INTERVAL 15 MINUTE)
and t2.id>t1.id
GROUP BY t1.card_id,t1.merchant_id
HAVING Count>2;
The query now works perfectly at counting cards.
Lastly, I am trying to add a column called SumAmount to also give a sum of the amounts. But SumAmount shows wrong logarithmically large amounts, because its a self-join. I think I would need the sum only on distinct row IDs, just like the Count column. How can SumAmount be fixed?
http://www.sqlfiddle.com/#!9/1bbd0/6
Your query is a little off, because it is missing the transaction id. But, your real issue is that you want to sum the amount from t2 and not t1:
SELECT t1.card_id, t1.merchant_id, t1.tran_dt,
count(distinct t2.id) + 1 as Count, sum(t2.amount) as SumAmount
FROM tran t1 INNER JOIN
tran t2
on t2.card_id = t1.card_id and
t2.merchant_id = t1.merchant_id and
t2.tran_dt >= t1.tran_dt and
t2.tran_dt <= DATE_ADD(t1.tran_dt, INTERVAL 15 MINUTE)
GROUP BY t1.id, t1.card_id, t1.merchant_id, t1.tran_dt
HAVING Count > 2;
Note: I changed the join condition from > to >= to include the first transaction. Also, I added the >= on the date, and changed the count in SELECT to be for the second table, not the first.
SELECT a.card_id, a.merchant_id, MAX(cases_count) AS cases_count, MAX(sum_of_amount) AS sum_of_amount
FROM
(
SELECT t1.id, t1.card_id,t1.merchant_id, count(*) AS cases_count, sum(t2.amount) AS sum_of_amount
FROM tran t1
INNER JOIN tran t2
ON t2.card_id=t1.card_id
AND t2.merchant_id=t1.merchant_id
AND (t2.tran_dt <= DATE_ADD(t1.tran_dt, INTERVAL 15 MINUTE)
AND t2.tran_dt >= t1.tran_dt )
GROUP BY t1.id, t1.card_id,t1.merchant_id
HAVING COUNT(*) > 2
) AS a
GROUP BY a.card_id, a.merchant_id

SQL Server 2008: LAG and runningTotals

CREATE TABLE #NetAmounts
(
priority int,
NetAmount decimal(10, 2),
DedicatedAmt decimal(10, 2)
)
INSERT INTO #NetAmounts (priority, NetAmount, DedicatedAmt)
VALUES (1, 6000, 2500),
(2, 6000, 2500),
(3, 6000, 2500),
(4, 6000, 2500)
Net Amount is always same throughout.
I want to update Dedicated Amount so that the sum of dedicated amount is always equals to the netamount by priority order
Expected Result:
Priority NetAmount DedicatedAmt
---------------------------------
1 6000 2500
2 6000 2500
3 6000 1000
4 6000 0
My query (not working)
;WITH CTE AS
(
SELECT
*,
RunningTotal = SUM(DedicatedAmt) OVER(ORDER BY 1)
FROM #NetAmounts
)
SELECT
Priority,
Netamount,
DedicatedAmt = CASE
WHEN DedicatedAmt > RunningTotal
THEN CASE
WHEN LAG(Netamount, 1, Netamount) OVER(ORDER BY Priority) >= DedicatedAmt
AND RunningTotal <= Netamount
THEN ABS(LAG(Netamount, 1, Netamount) OVER(ORDER BY Priority) - DedicatedAmt)
ELSE 0
END
ELSE DedicatedAmt
END
FROM CTE
Thanks

Two Limit Clauses in one statement?

I am trying to build a product review page where users can comment any submitted reviews like Amazon. I want the page to display 3 user reviews as well as 2 responses to each of the reviews if they exist.
Here's the table
CREATE TABLE product_review
(`ID` int, `username` varchar(21), `review_title` varchar(30), `review_or_reply` int)
;
INSERT INTO product_review
(`ID`, `username`, `review_title`, `review_or_reply`)
VALUES
(1, 'Tom', 'Rip-off', 0),
(2, 'Peter', 'Rip-off', 1),
(3, 'May', 'Rip-off', 1),
(4, 'June', 'Rip-off', 1),
(5, 'Tommy', 'Worth the Price', 0),
(6, 'Sammy', 'Worth the Price', 1),
(7, 'Sam', 'Worth the Price',1),
(8, 'Bryan','Worth the Price',1),
(9, 'Sally', 'Average Product', 0)
;
The review_or_reply field is effectively a Yes or No field, where 0 means it's a review and 1 is the review's comments by other users.
Is there a single select statement that can limit 3 reviews and bring up two of their comments? For example:
Select `username`,`review_title`,`reply` from product_review where review_or_reply ='0' Limit 3
Select `username`,`review_title`,`reply` from product_review where review_or_reply = '1' and title = 'Rip-off' Limit 2
Select `username`,`review_title`,`reply` from product_review where review_or_reply = '1' and title = 'Worth the Price' Limit 2
Select `username`,`review_title`,`reply` from product_review where review_or_reply = '1' and title = 'Average Product' Limit 2
I want the output to be like this:
username review_title review_or_reply
Tom Rip-off 0
Peter Rip-off 1
May Rip-off 1
Tommy Worth the Price 0
Sammy Worth the Price 1
Sam Worth the Price 1
Sally Average Product 0
this will return 3 review_titles and then pull out two responses to that
SELECT
pr.*,
IF( #A = t.review_title,
IF(#B = 3, #B := 1, #B := #B +1)
, #B
) AS group_col,
#A := t.review_title
FROM (
SELECT
id,
username,
review_title
FROM product_review
WHERE reply ='0' LIMIT 3
) t
JOIN product_review pr ON pr.review_title=t.review_title
CROSS JOIN (SELECT #A := "", #B := 1) AS temp
GROUP BY group_col, review_title
ORDER BY id;
EDIT:
if there are more than one reply that is 0 in the database like so then this query will check for that. (since you did specify in the other queries that the reply had to be 1).
INSERT INTO product_review
(`ID`, `username`, `review_title`, `reply`)
VALUES
(1, 'Tom', 'Rip-off', 0),
(2, 'Peter', 'Rip-off', 1),
(3, 'May', 'Rip-off', 0),
(4, 'June', 'Rip-off', 1),
(5, 'Tommy', 'Worth the Price', 0),
(6, 'Sammy', 'Worth the Price', 1),
(7, 'Sam', 'Worth the Price',1),
(8, 'Bryan','Worth the Price',1),
(9, 'Sally', 'Average Product', 0),
(10, 'Timothy', 'Rip-off', 1)
notice that at id 3 there is a reply of 0 with id 10 a reply of 1. this query will correctly skip the reply = 0.
SELECT
pr.*,
IF( #A = t.review_title,
IF(pr.reply = 0, 1,
IF(#B = 3, #B := 1, #B := #B +1)
), #B
) AS group_col,
#A := t.review_title
FROM (
SELECT
DISTINCT
id,
username,
review_title
FROM product_review
WHERE reply ='0'
GROUP BY review_title
LIMIT 3
) t
JOIN product_review pr ON pr.review_title=t.review_title
CROSS JOIN (SELECT #A := "", #B := 1) AS temp
GROUP BY group_col, review_title
ORDER BY id;
DEMO
...or slower but simpler...
SELECT x.*
FROM product_review x
JOIN product_review y
ON y.review_title = x.review_title
AND y.id <= x.id
GROUP
BY x.id
HAVING COUNT(*) <= 3
ORDER
BY MIN(y.id)
LIMIT 3;

Group by, with rank and sum - not getting correct output

I'm trying to sum a column with rank function and group by month, my code is
select dbo.UpCase( REPLACE( p.Agent_name,'.',' '))as Agent_name, SUM(convert ( float ,
p.Amount))as amount,
RANK() over( order by SUM(convert ( float ,Amount )) desc ) as arank
from dbo.T_Client_Pc_Reg p
group by p.Agent_name ,p.Sale_status ,MONTH(Reg_date)
having [p].Sale_status='Activated'
Currently I'm getting all total value of that column not month wise
Name amount rank
a 100 1
b 80 2
c 50 3
for a amount 100 is total amount till now but , i want get current month total amount not last months..
Maybe you just need to add a WHERE clause? Here is a minor re-write that I think works generally better. Some setup in tempdb:
USE tempdb;
GO
CREATE TABLE dbo.T_Client_Pc_Reg
(
Agent_name VARCHAR(32),
Amount INT,
Sale_Status VARCHAR(32),
Reg_date DATETIME
);
INSERT dbo.T_Client_Pc_Reg
SELECT 'a', 50, 'Activated', GETDATE()
UNION ALL SELECT 'a', 50, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'NotActivated', GETDATE()
UNION ALL SELECT 'c', 25, 'Activated', GETDATE()
UNION ALL SELECT 'c', 25, 'Activated', GETDATE()
UNION ALL SELECT 'c', 25, 'Activated', GETDATE()-40;
Then the query:
SELECT
Agent_name = UPPER(REPLACE(Agent_name, '.', '')),
Amount = SUM(CONVERT(FLOAT, Amount)),
arank = RANK() OVER (ORDER BY SUM(CONVERT(FLOAT, Amount)) DESC)
FROM dbo.T_Client_Pc_Reg
WHERE Reg_date >= DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP), 0)
AND Reg_date < DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) + 1, 0)
AND Sale_status = 'Activated'
GROUP BY UPPER(REPLACE(Agent_name, '.', ''))
ORDER BY arank;
Now cleanup:
USE tempdb;
GO
DROP TABLE dbo.T_Client_Pc_Reg;