SQL Server Multiple Groupings - sql

I am stumped by what seems like a simple problem.
We have the following Table.
ID--- ---Income--- ---Years Offset--- ---Income By Offset---
1 1000 1 NULL
2 500 1 NULL
3 400 1 NULL
4 0 1 NULL
5 2000 2 NULL
6 0 2 NULL
7 400 2 NULL
What I would love to figure out how to do is to sum all of the income column by the "Years Offset column" and place in the first row of the "Income by Offset column." What would be awesome is if the Income by Offset column has values of 1900 in row 1 and 2400 in row 5 with the rest of them rows being untouched.
I know that this sound like a simple problem. But I have tried Window functions, Row_number(), SELF joining tables and a piece of it is solved with each but am having trouble putting it all together.
Thanks in advance,
George

My Version of Your Table
DECLARE #yourTable TABLE (ID INT,Income INT,[Years Offset] INT,[Income By Offset] INT NULL);
INSERT INTO #yourTable
VALUES (1,1000,1,NULL),
(2,500,1,NULL),
(3,400,1,NULL),
(4,0,1,NULL),
(5,2000,2,NULL),
(6,0,2,NULL),
(7,400,2,NULL);
Actual Query
SELECT ID,
Income,
[Years Offset],
CASE
WHEN ID = MIN(ID) OVER (PARTITION BY [Years Offset])
THEN SUM(Income) OVER (PARTITION BY [Years Offset])
ELSE [Income By Offset]
END AS [Income By Offset]
FROM #yourTable
Results
ID Income Years Offset Income By Offset
----------- ----------- ------------ ----------------
1 1000 1 1900
2 500 1 NULL
3 400 1 NULL
4 0 1 NULL
5 2000 2 2400
6 0 2 NULL
7 400 2 NULL

This should return the required result set:
SELECT ID, Income, [Years Offset],
CASE WHEN ROW_NUMBER() OVER (PARTITION By [Years Offset]
ORDER BY ID) = 1
THEN SUM(Income) OVER (PARTITION BY [Years Offset])
ELSE NULL
END AS [Income By Offset]
FROM mytable
Windowed version of SUM calculates the Income per [Years Offset]. ROW_NUMBER() is used to return this value only for the first row of each [Years Offset] group.
Demo here

How about:
SQL Fiddle
MS SQL Server 2014 Schema Setup:
CREATE TABLE Income
(
ID INT PRIMARY KEY,
Income INT NOT NULL,
YearsOffset Int NOT NULL,
IncomeByOffset INT NULL
)
INSERT INTO Income (ID, Income, YearsOffset)
VALUES (1,1000,1),
(2,500,1),
(3,400,1),
(4,0,1),
(5, 2000, 2),
(6,0,2),
(7,400,2)
Query 1:
UPDATE Income
SET IncomeByOffset = I.IncomeByOffset
From
(
SELECT YearsOffset, SUM(Income) As IncomeByOffset, Min(ID) As MinId
FROM Income
GROUP BY YearsOffset
) I
WHERE Income.YearsOffset = I.YearsOffset
AND Income.Id = I.MinId
Results:
Query 2:
SELECT *
FROM Income;
Results:
| ID | Income | YearsOffset | IncomeByOffset |
|----|--------|-------------|----------------|
| 1 | 1000 | 1 | 1900 |
| 2 | 500 | 1 | (null) |
| 3 | 400 | 1 | (null) |
| 4 | 0 | 1 | (null) |
| 5 | 2000 | 2 | 2400 |
| 6 | 0 | 2 | (null) |
| 7 | 400 | 2 | (null) |

Related

query for column that are within a variable + or 1 of another column

I have a table that has 2 columns, and I am trying to determine a way to select the records where the two columns are CLOSE to one another. Maybe based on standard deviation if i can think about how to do that. But for now, this is what my table looks like:
ID| PCT | RETURN
1 | 20 | 1.20
2 | 15 | 0.90
3 | 0 | 3.00
The values in the pct field is a percent number (for example 20%). The value in the return field is a not fully calculated % number (so its supposed to be 20% above what the initial value was). The query I am working with so far is this:
select * from TABLE1 where ((pct = ((return - 1)* 100)));
What I'd like to end up with are the rows where both are within a set value of each other. For example If they are within 5 points of each other, then the row would be returned and the output would be:
ID| PCT | RETURN
1 | 20 | 1.20
2 | 15 | 0.90
In the above, ID 1 should work out to be PCT = 20 and Return = 20, and ID 2, is PCT = 15 and RETURN = 10. Because it was within 5 points of each other, it was returned.
ID 3 was not returned because 0 and 200 are way above the 5 point threshold.
Is there any way to set a variable that would return a +- 5 when comparing the two values from the above attributes? Thanks.
RexTester Example:
Use Lead() over (Order by PCT) to look ahead and LAG() to look back to the next row do the math and evaluate results...
WITH CTE (ID, PCT , RETURN) as (
SELECT 1 , 20 , 1.20 FROM DUAL UNION ALL
SELECT 2 , 15 , 0.90 FROM DUAL UNION ALL
SELECT 3 , 0 , 3.00 FROM DUAL),
CTE2 as (SELECT A.*, LEAD(PCT) Over (ORDER BY PCT) LEADPCT, LAG(PCT) Over (order by PCT) LAGPCT
FROM CTE A)
SELECT * FROM CTE2
WHERE LEADPCT-PCT <=5 OR PCT-LAGPCT <=5
Order by ID
Giving us:
+----+----+-----+--------+---------+--------+
| | ID | PCT | RETURN | LEADPCT | LAGPCT |
+----+----+-----+--------+---------+--------+
| 1 | 1 | 20 | 1,20 | NULL | 15 |
| 2 | 2 | 15 | 0,90 | 20 | 0 |
+----+----+-----+--------+---------+--------+
or use the return value instead of PCT... just depends on what you're after. But maybe I don't fully understand the question..

Sum (or count) multiple case statement

I have the following table:
Month | Item | Events | Party | Spirit | Faith |
May | 123 | 1 | 1 | 0 | 0 |
June |123 | 1 | 0 | 1 | 1 |
it is basically 1 for yes 0 for no. I need to know how many different categories each item is in each month
I need the following results:
Month | Item | Counts |
May | 123 | 2 |
June| 123 | 3 |
This is NOT working:
select Month, Item,
sum(case when EVENTS = 1 then 1 when PARTY = 1 then 1 when SPIRIT = 1 then 1 when FAITH = 1 then 1 else 0 end) as Counts
from TABLE
group by 1,2
Please help, thanks!
You don't need aggregation:
select Month, Item,
(events + party + spirit + faith) as counts
from t;
CREATE TABLE #T
(
Month varchar(10), Item int, Events bit, Party bit, Spirit bit , Faith bit
)
insert into #T
SELECT 'May' , 123 , 1 , 1 , 0 , 0 union
SELECT 'June' ,123 , 1 , 0 , 1 , 1
select Month, Item, CAST(Events AS INT) + CAST(Party AS INT)+ CAST(Spirit AS
INT) +CAST(Faith AS INT) from #T
Aggregation is not needed. Since the events, party, spirit and faith are bit columns, we need to cast it to int and then add it.

Count who paid group by 1, 2 or 3+

I have a payment table like the example below and I need a query that gives me how many IDs paid (AMOUNT > 0) 1 time, 2 times, 3 or more times. Example:
+----+--------+
| ID | AMOUNT |
+----+--------+
| 1 | 50 |
| 1 | 0 |
| 2 | 10 |
| 2 | 20 |
| 2 | 15 |
| 2 | 10 |
| 3 | 80 |
+----+--------+
I expect the result:
+-----------+------------+-------------+
| 1 payment | 2 payments | 3+ payments |
+-----------+------------+-------------+
| 2 | 0 | 1 |
+-----------+------------+-------------+
ID 1: Paid 1 time (50). The other payment is 0, so I did not count. So, 1 person paid 1 time.
ID 2: Paid 3 times (10,20,15). So, 1 person paid 3 or more time.
ID 3: Paid 1 time (80). So, 2 persons paid 1 time.
I'm doing manually on excel right now but I'm pretty sure there is a more practical solution. Any ideas?
A little sub-query will do the trick
Declare #YOurTable table (ID int, AMOUNT int)
Insert into #YourTable values
( 1 , 50 ),
( 1 , 0) ,
( 2 , 10) ,
( 2 , 20) ,
( 2 , 15) ,
( 2 , 10) ,
( 3 , 80)
Select [1_Payment] = sum(case when Cnt=1 then 1 else 0 end)
,[2_Payment] = sum(case when Cnt=2 then 1 else 0 end)
,[3_Payment] = sum(case when Cnt>2 then 1 else 0 end)
From (
Select id
,Cnt=count(*)
From #YourTable
Where Amount<>0
Group By ID
) A
Returns
1_Payment 2_Payment 3_Payment
2 0 1
To get the output you want try using a table to form the data and then SELECT from that:
with c as (
select count(*) count from mytable where amount > 0 group by id)
select
sum(case count when 1 then 1 else 0 end) "1 Payment"
, sum(case count when 2 then 1 else 0 end) "2 Payments"
, sum(case when count > 2 then 1 else 0 end) "3 Payments"
from c
Here is an example you can play with to see how the query is working.

How to create sql selection based on condition?

I have the following database which shows characteristics of attributes as follows:
attributeId | attributeCode | groupCode
------------+---------------+-----------
1 | 10 | 50
1 | 10 | 50
1 | 12 | 50
My desired result from a select would be:
attributeId | groupcount | code10 | code12
------------+------------+--------+--------
1 | 1 | 2 | 1
Which means: attributeId = 1 has only one groupCode (50), where attributeCode=10 occurs 2 times and attributeCode=12 occurs 1 time.
Of course the following is not valid, but you get the idea of what I'm trying to achieve:
select attributeId,
count(distinct(groupCode)) as groupcount,
attributeCode = 10 as code10,
attributeCode = 12 as code12
from table
group by attributeId;
Try this:
SELECT attributeId, COUNT(DISTINCT groupCode) AS groupcount,
COUNT(CASE WHEN attributeCode = 10 THEN 1 END) AS code10,
COUNT(CASE WHEN attributeCode = 12 THEN 1 END) AS code12
FROM mytable
GROUP BY attributeId
Demo here

Sql Query Compare and Sum

I have these problem I need to match the sum a columns to see if they match with the Final Total of the Invoice by Invoice Number ( I am working in a query to do it)
Example
Invoice No Line _no Total Line Invoice total Field I will create
----------------------------------------------------------------------
45 1 145 300 145
45 2 165 300 300 Match
46 1 200 200 200 Match
47 1 100 300 100
47 2 100 300 200
47 3 100 300 300 Match
You want a cumulative sum. In SQL Server 2012+, just do:
select e.*,
(case when InvoiceTotal = sum(InvoiceTotal) over (partition by invoice_no order by line_no)
then 'Match'
end)
from example e;
In earlier versions of SQL Server, I would be inclined to do it with a correlated subquery:
select e.*
(case when InvoiceTotal = (select sum(InvoiceTotal)
from example e2
where e2.Invoice_no = e.invoice_no and
e2.line_no >= e.line_no
)
then 'Match'
end)
from example e;
You can also do this with a cross apply as M Ali suggests.
EDIT:
Now that I think about the problem, you don't need a cumulative sum. That was just how I originally thought of the problem. So, this will work in SQL Server 2008:
select e.*,
(case when InvoiceTotal = sum(InvoiceTotal) over (partition by invoice_no)
then 'Match'
end)
from example e;
You can't get the cumulative sum out (the second to last column) without more manipulation, but the match column is not hard.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE TEST(InvoiceNo INT, Line_no INT, TotalLine INT, InvoiceTotal INT)
INSERT INTO TEST VALUES
(45 ,1 ,145 ,300),
(45 ,2 ,165 ,300),
(46 ,1 ,200 ,200),
(47 ,1 ,100 ,300),
(47 ,2 ,100 ,300),
(47 ,3 ,100 ,300)
Query 1:
SELECT t.[InvoiceNo]
,t.[Line_no]
,t.[TotalLine]
,t.[InvoiceTotal]
,C.Grand_Total
,CASE WHEN C.Grand_Total = t.[InvoiceTotal]
THEN 'Match' ELSE '' END AS [Matched]
FROM TEST t
CROSS APPLY (SELECT SUM([TotalLine]) AS Grand_Total
FROM TEST
WHERE [InvoiceNo] = t.[InvoiceNo]
AND [Line_no] < = t.[Line_no]) C
Results:
| INVOICENO | LINE_NO | TOTALLINE | INVOICETOTAL | GRAND_TOTAL | MATCHED |
|-----------|---------|-----------|--------------|-------------|---------|
| 45 | 1 | 145 | 300 | 145 | |
| 45 | 2 | 165 | 300 | 310 | |
| 46 | 1 | 200 | 200 | 200 | Match |
| 47 | 1 | 100 | 300 | 100 | |
| 47 | 2 | 100 | 300 | 200 | |
| 47 | 3 | 100 | 300 | 300 | Match |
Is this what you're looking for? I think subquery is what you're asking about, but i'm guessing to get an end result similar to the entire thing.
select t."Invoice No", t."Line no_", t."Invoice total",
calcTotals.lineNum as calcSum, case when t."Invoice total" = calcTotals.lineNum then 'matched' else 'not matched' end
from [table] t
inner join (
select "Invoice No" as invoiceNumber,
sum("Line _no") as lineNum
from [table]
group by "Invoice No"
) calcTotals on t."Invoice No" = calcTotals.invoiceNumber