sum(Amount} only totals positive numbers in my table. What am I doing wrong? - sql-server-2005

I have a table that contains hours for jobs technicians perform. The majority of the entries are positive but occasionally there is a credit to the customer that show as negative hours. My query includes sum(Amount) but only sums the positive numbers. It should sum 2 + 2 - 4 as 0, but it's giving me a total of 4.
Example of table
job no. Amount
j211 2
j211 2
j211 -4
select job no. sum(Amount)
from service.jobs
where job no. = 'j211'
group by job no.
This returns a total Amount for j211 of 4, not 0.

how about this one. try this one. and compare your table with this one.
CREATE TABLE [dbo].[service](
[jobno] [varchar](5) NULL,
[amount] [smallint] NULL
) ON [PRIMARY]
GO
Insert into service(jobno,amount)
Select 'j211',2 UNION ALL
Select 'j211',2 UNION ALL
Select 'j211',-4
select sum(Amount)
from service
where jobno = 'j211'
group by jobno

Related

how can I count some values for data in a table based on same key in another table in Bigquery?

I have one table like bellow. Each id is unique.
id
times_of_going_out
fef666
2
S335gg
1
9a2c50
1
and another table like this one ↓. In this second table the "id" is not unique, there are different "category_name" for a single id.
id
category_name
city
S335gg
Games & Game Supplies
tk
9a2c50
Telephone Companies
os
9a2c50
Recreation Centers
ky
fef666
Recreation Centers
ky
I want to find the difference between destinations(category_name) of people who go out often(times_of_going_out<5) and people who don't go out often(times_of_going_out<=5).
** Both tables are a small sample of large tables.
 ・ Where do people who go out twice often go?
 ・ Where do people who go out 6times often go?
Thank you
The expected result could be something like
less than 5
more than 5
top ten “category_name” for uid’s with "times_of_going_out" less than 5 times
top ten “category_name” for uid’s with "times_of_going_out" more than 5 times
Steps:
combining data and aggregating total time_going_out
creating the categories that you need : less than equal to 5 and more than 5. if you don't need equal to 5, you can adjust the code
ranking both categories with top 10, using dense_rank(). this will produce the rank from 1 - 10 based on the total time_going out
filtering the cases so it takes top 10 values for both categories
with main as (
select
category_name,
sum(coalesce(times_of_going_out,0)) as total_time_per_category
from table1 as t1
left join table2 as t2
on t1.id = t2.id
group by 1
),
category as (
select
*,
if(total_time_per_category >= 5, 'more than 5', 'less than equal to 5') as is_more_than_5_times
from main
),
ranking_ as (
select *,
case when
is_more_than_5_times = 'more than 5' then
dense_rank() over (partition by is_more_than_5_times order by total_time_per_category desc)
else NULL
end AS rank_more_than_5,
case when
is_more_than_5_times = 'less than equal to 5' then
dense_rank() over (partition by is_more_than_5_times order by total_time_per_category)
else NULL
end AS rank_less_than_equal_5
from category
)
select
is_more_than_5_times,
string_agg(category_name,',') as list
from ranking_
where rank_less_than_equal_5 <=10 or rank_more_than_5 <= 10
group by 1

How to use group by with pivot in Oracle 11g

Table Structure
CREATE TABLE PLAN
(
plan_id NUMERIC,
startdate DATE NOT NULL,
enddate DATE,
cost NUMERIC (3) NOT NULL,
plan_type VARCHAR (5) NOT NULL,
CONSTRAINT plan_pk PRIMARY KEY (plan_id),
CONSTRAINT plan_ck CHECK (startdate <= enddate)
);
Requirement:
Create a pivot table to list the number of plans by plan type (voice, text, data) and also by cost range. For cost range, create 3 segments, “Economy Plans” defined as those with monthly fee less than $40, “Standard Plans” defined as plans with monthly fees between $40 and $50, and “Premium Plans” defined as those with monthly fees more than $50. The pivot table result should include 3 columns (Voice, Data, Text) and 3 rows (Economy Plans, Standard Plans, Premium Plans). The cells should contain the counts of each and should have 0 rather than NULL where there are no matching plans.
MY query SO FAR
SELECT * FROM
(SELECT cost, plan_type
FROM plan)
PIVOT ( count(plan_type) FOR plan_type IN ('voice', 'data', 'text')
);
it return count of voice, data, text type plans for all cost. I am unable to get it in range as specified in the requirement.
MY other try
SELECT CASE
WHEN cost < 40 THEN '1-40'
WHEN cost<=50 THEN '40-50'
ELSE '50+'
END AS cost, count('voice'), count('data'), count('text') FROM
(SELECT cost, plan_type
FROM plan)
PIVOT (
count(plan_type) FOR plan_type IN ('voice', 'data', 'text')
)
GROUP BY CASE
WHEN cost < 40 THEN '1-40'
WHEN cost<=50 THEN '40-50'
ELSE '50+'
END;
This query even count 1 for 0 value from pivot table thus returning a wrong answer. Please suggest how should I proceed about it?
You're almost there if you combine the two attempts:
SELECT * FROM (
SELECT case
when cost < 40 then 'Economy Plans'
when cost <= 50 then 'Standard Plans'
else 'Premium Plans'
end as cost_range,
plan_type
FROM plan
)
PIVOT (
count(plan_type) FOR plan_type IN ('voice' as voice, 'data' as data, 'text' as text)
);
The subquery assigns a text label to the range, and the pivot is then done against that range rather than the actual plan costs.
With some made-up data that gives a result like:
COST_RANGE VOICE DATA TEXT
-------------- ---------- ---------- ----------
Standard Plans 1 3 0
Economy Plans 2 1 1
Premium Plans 3 2 0

How can I produce rows that sum subgroups of rows, without grouping?

Let's say I have a table like this:
ID Income Expenses
-------------------------
1 $1000 $200
1 $2000 $400
2 $500 $200
2 $100 $60
I'd like to add one row per ID that sums the ID's Income and Expenses. For example:
ID Income Expenses
-------------------------
1-SUM $3000 $600
1 $1000 $200
1 $2000 $400
2-SUM $600 $260
2 $500 $200
2 $100 $60
Is this possible with SQL? I read about OVER and PARTITION BY but that'll add a new column, not a new row. Might a UNION be the best way to go here?
Try this
create table yourtable(id int,income int,expenses int)
insert into yourtable values (1,100,200),(1,150,250),(2,200,300),(2,250,350)
with CTE
as
(select ID,sum(income) as income,sum(expenses) as expenses,1 as isSum
from yourtable
group by id
)
select * from (
select *,0 as isSum from yourtable
union all
select * from CTE ) as t
order by id,issum
output
Using GROUP BY:
SQLFiddleDemo
CREATE TABLE tab(id INT , income INT , expenses INT);
INSERT INTO tab(id, income, expenses)
VALUES (1, 1000, 200),(1, 2000, 400),(2, 500, 200),(2, 100, 60)
SELECT id,
[income] = SUM(income),
[expenses] = SUM(expenses),
[grouping_level] = GROUPING_ID(id, income, expenses) /* 0 - actual data , 3 - group based on column income, expenses*/
FROM tab
GROUP BY GROUPING SETS ((id), (id,income, expenses))
Why grouping is useful:
Using GROUPING SETS gives great flexibility for instance when you want total sum use: GROUP BY GROUPING SETS ((id), (id,income, expenses), ())
GROUPING SETS can be combined with ROLLUP/CUBE.
GROUPING_ID indicates grouping level.
Number works like binary. GROUPING(a, b, c) if b i c is used for grouping you get 011B => 3

How can I spread a value across multiple rows without a cursor?

Is there an easy way to spread a value across multiple rows?
For example, my table contains
Type Invoiced Paid Current
Charge 100 0 100
Charge 100 0 100
Charge 100 0 100
Payment 0 250 0
Payment 0 25 0
The data is imported this way, but I need to populate the Current and Paid columns with whatever they should be for that transaction based on the payment transactions that were also imported.
Is there an easy way to write a query to determine the balance for the Current column for each record?
For example, the 250 would apply 100 for the first two records and 50 to the next two, and the 25 would get applied to the last one, so the end result after updating the Current balance in my table should be:
Type Invoiced Paid Current
Charge 100 100 0
Charge 100 100 0
Charge 100 75 25
Payment 0 250 0
Payment 0 25 0
I'd ideally like to do this with a single query instead of using a cursor to process each item individually. I've been trying to do it by using the Row_Number() function and joining two subqueries, but I know I'm missing something here
Here was my first attempt, which resulted in getting the running total of the current balance
;with cte(invoiced, paid, current)
as (
select invoiced, paid, current
, row_number() over (order by datecreated)
from mytable
)
select t1.invoiced, t1.paid, sum(t2.invoiced - t2.paid) as [current]
from cte as t1
join cte as t2 on t1.number = t2.number and t2.rownum <= t1.rownum
group by t1.uid, t1.number, t1.rownum
order by t1.rownum
Result:
Invoiced Paid Current
100 0 100
100 0 200
100 0 300
0 250 50
0 25 25
I'm sure there's a way to do this, but right now my brain seems on strike and is refusing to come up with a solution.
I think I found a solution
First off, I don't need to link the Paid transactions to the Invoiced transactions, so I only need the sum of all payments
select accountid, sum(paid)
from mytable
where type = 'Payment'
group by accountid
Then I need to apply this value to each record until the running total becomes greater than the total paid.
To do this, I modified my running total query so it only sums the charges instead of summing both charges and payments
;with cte(id, accountid, invoiced, paid, current)
as (
select id, accountid, invoiced, paid, current
, row_number() over (order by datecreated)
from mytable
where type = 'Charge'
)
select t1.id, t1.accountid, t1.invoiced, sum(t2.invoiced) as [runningTotalOfCharges]
from cte as t1
join cte as t2 on t1.number = t2.number and t2.rownum <= t1.rownum
group by t1.id, t1.accountid, t1.invoiced
and joined that to the payment query, so now I have a bunch of rows containing the total payment amount, the running total of charges up until that record, and the current record's charge amount.
From there, I just needed a CASE statement to determine if the charge was fully paid, partially paid, or not paid at all, and use a little math to figure out the Paid and Current records
select charged.Id, charged.AccountId, charged.Invoiced
-- Use Case statements to determine if this payment is fully paid, partially paid,
-- or not paid at all, then determine Current and Paid based on that
, case when totalpaid - runningtotal >= 0 then invoiced
when invoiced > abs(totalpaid - runningtotal) then invoiced + totalpaid - runningtotal
else 0 end as [Paid]
, case when totalpaid - runningtotal >= 0 then 0
when invoiced > abs(totalpaid - runningtotal) then abs(totalpaid - runningtotal)
else invoiced end as [Current]
from
(
-- Running total query from above
select t1.id, t1.accountid, t1.invoiced, sum(t2.invoiced) as [runningtotal]
from cte as t1
join cte as t2 on t1.number = t2.number and t2.rownum <= t1.rownum
group by t1.id, t1.accountid, t1.invoiced
) as charged
inner join (
-- Total Paid query from above
select accountid, sum(paid) as totalpaid
from mytable
where type = 'Payment'
group by accountid
) as paid on charged.number = paid.number
And the end result is what I want. Just need to join this to the actual data table via the Id column, and update the Paid and Current values :)
Id AccountId Invoiced Paid Current
1 1 100 100 0
2 1 100 100 0
3 1 100 75 25
You could calculate running totals and sums for the paid and current columns and then perform some math to get the current column values. I gave it a try but got bogged down in the math to come up with the final values. It's not an easy way, but it's a way.
DECLARE #myTable TABLE
(
[TranId] INT,
[Type] VARCHAR(10),
[Invoiced] INT,
[Paid] INT,
[Current] INT,
[SumPaid] INT,
[SumCurrent] INT,
[RunningPaid] INT,
[RunningCurrent] INT
)
INSERT INTO #myTable SELECT 1, 'Charge', 100, 0, 100, null, null, null, null
INSERT INTO #myTable SELECT 2, 'Charge', 100, 0, 100, null, null, null, null
INSERT INTO #myTable SELECT 3, 'Charge', 100, 0, 100, null, null, null, null
INSERT INTO #myTable SELECT 4, 'Paid', 0, 250, 0, null, null, null, null
INSERT INTO #myTable SELECT 5, 'Paid', 0, 25, 0, null, null, null, null
UPDATE #myTable SET SumPaid = (SELECT SUM([Paid]) FROM #myTable)
UPDATE #myTable SET SumCurrent = (SELECT SUM([Current]) FROM #myTable)
UPDATE #myTable
SET
[RunningPaid] = full_running_total_set.[RunningPaid],
[RunningCurrent] = full_running_total_set.[RunningCurrent]
FROM #myTable
INNER JOIN
(
SELECT
TranId1,
SUM([Paid]) AS [RunningPaid],
SUM([Current]) AS [RunningCurrent]
FROM
(
SELECT
set2.[Paid],
set2.[Current],
set1.[TranId] AS [TranId1],
set2.[TranId] AS [TranId2]
FROM #myTable set1
INNER JOIN #myTable set2
ON set1.[TranId] >= set2.[TranId]
)running_total_set
GROUP BY [TranId1]
)full_running_total_set
ON [TranId] = full_running_total_set.[TranId1]
SELECT * FROM #myTable

How to list posts from one table and at the same time sum an amount from another table?

First of all, hope my English isn't too bad..
I have a problem I can't solve.
The thing is that I am thinking about an order handling system..
What I have so far is a table where every article registrats that belong to an order like this
Tbl_orderLines
id, prod_week, article_id
Then i also have och table where the amount for each row in tbl_orderLines registrates like this
Tbl_inventory
orderline_id, article_id, amount
Now article_id in both tables are the same (double post) thought it would be easier to do so?
What I want to is the get the available amount of each specific article at a specific time.
So I want to be able to list every row in Tbl_orderLInes and with a join get the amount for every row, but! I also want to get the total amount of a specific article. Therefore, I want to sum the amount column from Tbl_inventory where the article_id is the same as the row from tbl_orderLines.
This is what I've got now
Tbl_orderLines
id prod_week article_id
1 1130 2
2 1129 5
3 1129 2
4 1128 2
Tbl_inventory
orderLine_id article_id amount
1 2 1
2 5 2
3 2 1
4 2 2
The result should be something like
id prod_week article_id amount TotalAmount
1 1130 2 1 4
2 1129 5 2 2
3 1129 2 1 3
4 1128 2 2 2
Don't know if its possible to understand my question?
I'm using MSSQL..
Much appreciate any help..
I believe the first part of your result set is achieve by the following.
SELECT
[order_line].id,
[order_line].prod_week,
[order_line].article_id,
[inventory].amount
FROM
Tbl_orderLines AS [order_line]
INNER JOIN
Tbl_inventory AS [inventory]
ON [inventory].orderline_id = [order_line].id
I'm not 100% certain about the TotalAmount column, however. My best guess is that you want...The sum of the amounts for all preceeding weeks with the same article_id?
SELECT
[order_line].id,
[order_line].prod_week,
[order_line].article_id,
[inventory].amount,
SUM([historic_inventory].amount) AS TotalAmount
FROM
Tbl_orderLines AS [order_line]
INNER JOIN
Tbl_inventory AS [inventory]
ON [inventory].orderline_id = [order_line].id
INNER JOIN
Tbl_orderLines AS [historic_orders]
ON [historic_orders].article_id = [order_line].article_id
AND[historic_orders].prod_week <= [order_line].prod_week
INNER JOIN
Tbl_inventory AS [historic_inventory]
ON [historic_inventory].orderline_id = [historic_order_line].id
GROUP BY
[order_line].id,
[order_line].prod_week,
[order_line].article_id,
[inventory].amount
I can't get exactly what you want but I believe you can try something like this enhance the idea towards your goal.
CREATE TABLE [dbo].[inventory](
[id] [int] NULL,
[article_id] [int] NULL,
[amount] [int] NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[orderLines](
[id] [int] NULL,
[prod_week] [int] NULL,
[article_id] [int] NULL
) ON [PRIMARY]
GO
SELECT ol.id,
ol.prod_week,
ol.article_id,
i.amount,
SUM(amount) OVER (PARTITION BY i.article_id) AS totalAmount
FROM dbo.orderLines AS ol, dbo.inventory AS i
WHERE ol.id = i.id
ORDER BY ol.id