How to sum up a field depending on an other field value? - sql

I am developing a small stored procedure on SQL server 2008. I have little knowledge on SQL queries, but enough to achieve simple tasks. However I came up with a problem I can't solve myself. Before I start explaining my problem, please pardon me if I spell a SQL query-word wrong because I am not a native English speaker.
I have 4 fields(CSV representation):
ID, NAME, VALUES, ANSWER
25, Tom , 2400 , 0
25, Tom , 600 , 0
25, Tom , 500 , 1
25, Tom , 300 , 1
27, Jerry, 100, 0
27, Jerry, 20, 1
27, Jerry, 60, 1
27, Jerry, 2000, 0
What I want to do is group by the selection by its ID and NAME, Sum up it's values in a field named positive when ANSWER = 1 and negative when ANSWER = 0.
ID, NAME, SUM, NEGATIVE, POSITIVE
25, Tom, 3000, 800
27, Jerry, 2100, 80
I Guess my question has been asked several times, but I wasn't able to find anything about it, probably because I am using the wrong terms. Anyway if someone could help that would save me a lot of time.

You'll do so with a CASE statement.
select Id
, Name
, SUM(case when answer = 1 then Values else 0 end) as Positive
, SUM(case when answer = 0 then Values else 0 end) as Negative
from MyTable
group by Id
, Name

Rewrite your SQL with columns such as this:
sum(case when answer=0 then values else 0 end) negative

Ephismen,
The example you give has a hidden problem in it. Will you always have at least one positive and one negative? What do you want to happen if you only have one or the other and not both. Joining the table to itself will not work when you have multiple rows for each id and name.
A UNION of two separate queries will be able to answer these questions, but not sure if it applies for SQL server 2008.
You may be able to achieve this by using the group by clause for SQL as follows:
select id
, name
, sum (neg_values) as negative
, sum (pos_values) as positive
from -- temporary table
( select id
, name
, sum (values) as neg_values
, 0 as pos_values -- placeholder
from mytable
where answer = 0 -- negative
group by id
, name
union all
select id
, name
, 0 as neg_values -- placeholder
, sum (values) as pos_values
from mytable
where answer = 1 -- positive
group by id
, name
)
group by id
, name
The temporary table (inner select with union) will return rows similar to this:
id name neg_value pos_value
25 tom 3000 0
25 tom 0 800
27 jerry 2100 0
27 jerry 0 80
The final select will return your desired results (summing them together).

Related

Finding the sum and difference of values with the same ID in SQL

I have a dataframe with only positive values that need to be subtracted with one another, so the initial df looks like:
date
ID
value
2022/05/31
1
100000
2022/05/31
2
20000
2022/05/31
3
20000
2022/05/31
5
50000
2022/05/31
6
10000
2022/05/31
7
10000
I need to take the sum of ID's 2,3 and subtract it from ID 1. Also, i need to take the sum of ID's 6,7 and subtract it from ID 5.
Therefore, the remaining dataframe would have only two values: 60,000 (100,000 - 20,000 - 20,000) and 30000 (50,000 - 10,000 - 10,000).
The desired output would be the following dataframe:
date
ID
value
2022/05/31
1
60000
2022/05/31
2
30000
So far I have only been able to take the sum of one set of ID's, but then unable to subtract them:
date
ID
value
2022/05/31
1
100000
2022/05/31
2
40000
So I have been able to subtotal ID 2 & 3, but don't know how to then subtract it from ID 1, then to the same thing with ID's 5 through 7
Would be grateful for any help, thanks
FWIW, when you post SQL questions its often helpful to include the RDBMS (mySQL, Teradata, Oracle, Snowflake, etc) because SQL syntax is slightly different across all these systems.
I used Rasgo to generate this SQL (which works in Snowflake), but depending on your system you can probably tweak this to work.
You said in the comments that you wanted an answer that works exactly for this set of ID's, so it can be hard-coded. In that case,
WITH CTE_CONFIG AS (
SELECT
*,
CASE WHEN ID IN('1', '5') THEN 'START' WHEN ID IN('2', '3', '6', '7') THEN 'SUBTRACT' ELSE NULL END AS OPERATION_TO_DO,
CASE WHEN ID IN('1', '2', '3') THEN 1 WHEN ID IN('5', '6', '7') THEN 2 ELSE NULL END AS GROUP_ID
FROM
{{ your_table }}
),
CTE_AGG AS (
SELECT
GROUP_ID,
OPERATION_TO_DO,
SUM(MYVALUE) as MYVALUE_SUM
FROM
CTE_CONFIG
GROUP BY
GROUP_ID,
OPERATION_TO_DO
)
SELECT
GROUP_ID,
SUM_START - SUM_SUBTRACT AS ANSWER
FROM
(
SELECT
GROUP_ID,
SUM(
CASE WHEN OPERATION_TO_DO = 'START' THEN MYVALUE_SUM ELSE 0 END
) AS SUM_START,
SUM(
CASE WHEN OPERATION_TO_DO = 'SUBTRACT' THEN MYVALUE_SUM ELSE 0 END
) AS SUM_SUBTRACT
FROM
CTE_AGG
GROUP BY
GROUP_ID
) rs
The theory here is that you establish 2 new columns with CASE statements. The first, OPERATION_TO_DO will determine whether you are using that ID in the SUBTRACT portion, or the START portion of your equation.
The next column to establish is the GROUP_ID, which in your case is ID 1,2,3 and 5,6,7.
Next, we can aggregate the values by GROUP_ID and OPERATION_TO_DO. Once that's done, we can simply do the math formula that you want.
I thought that you might have a systematic reason for choosing ID 1 and 5, like perhaps it is every 3 records that the cycle starts over. That is certainly possible as well, but since you answered that you wanted to hard-code this, I didn't solve it that way.
If for some reason this syntax doesn't work, I suggest posting a new question with this SQL, tagging the version of SQL you're using, and the error message that you're getting.

Oracle SQL Count function

I am hoping someone can advise on the below please?
I have some code (below), it is pulling the data I need with no issues. I have been trying (in vain) to add a COUNT function in here somewhere. The output I am looking for would be a count of how many orders are assigned to each agent. I tried a few diffent things based on other questions but can't seem to get it correct. I think I am placing the COUNT 'Agent' statement and the GROUP BY in the wrong place. Please can someone advise? (I am using Oracle SQL Developer).
select
n.ordernum as "Order",
h.employee as "Name"
from ordermgmt n, orderheader h
where h.ordernum = n.ordernum
and h.employee_group IN ('ORDER.MGMT')
and h.employee is NOT NULL
and n.percentcomplete = '0'
and h.order_status !='CLOSED'
Output I am looking for would be, for example:
Name Orders Assigned
Bob 3
Peter 6
John 2
Thank you in advance
Name
Total
49
49
49
49
49
John
4
John
4
John
4
John
4
Peter
2
Peter
2
Bob
3
Bob
3
Bob
3
for example. so there are 49 blank rows summed up as 49 in the Total column. I did not add the full 49 blank columns to save space
Would be easier with sample data and expected output, but maybe you are looking for something like this
select
n.ordernum as "Order",
h.employee as "Name",
count(*) over (partition by h.employee) as OrdersAssigned
from ordermgmt n, orderheader h
where h.ordernum = n.ordernum
and h.employee_group IN ('ORDER.MGMT')
and h.employee is NOT NULL
and n.percentcomplete = '0'
and h.order_status !='CLOSED'
The use of COUNT (as other aggregate functions) is simple.
If you want to add an aggregate function, please group all scalar fields in the GROUP BY clause.
So, in the SELECT you can manage field1, field2, count(1) and so on but you must add in group by (after where conditions) field1, field2
Try this:
select
h.employee as "Name",
count(1) as "total"
from ordermgmt n, orderheader h
where h.ordernum = n.ordernum
and h.employee_group IN ('ORDER.MGMT')
and h.employee is NOT NULL
and n.percentcomplete = '0'
and h.order_status !='CLOSED'
GROUP BY h.employee

How can I read the first n rows of table a based upon the number of rows in table b?

I have a table "L20" that contains 1 to 20 values "HDIF" in it, sorted in ascending order. I need to extract the first 1 to 10 of those values into table "T10" depending upon the number of values in table "L20". I'm using Windows 10, Libreoffice 6.4.4, with Firebird 3 database. I've tried the CASE statement and the DECODE statement on the COUNT of rows in "L20", but neither seems to work.
If I put in a numeral for the SELECT on table "L20" then it works correctly. Anyone have an idea of how to solve? The purpose of this query is to calculate a golf handicap which uses [up to] the best (lowest) 10 scores of [up to] the last (most recent) 20 games played. Here is the coding:
/* Qry_Index_Calc - calculates handicap index from top 10 differentials of last 20 games */
/* Source is "VW_Plyr_Diff" which has handicap differentials already calculated. */
SELECT (AVG ("T10"."HDIF") * .96) "Index", (Count ("T10"."HDIF")) FROM
/* Get only the games needed if less than 20 games have been played. */
(
SELECT FIRST
DECODE ((SELECT COUNT (*) FROM "L20"),
1, 1
, 2, 1
, 3, 1
, 4, 1
, 5, 1
, 6, 1
, 7, 2
, 8, 2
, 9, 3
, 10, 3
, 11, 4
, 12, 4
, 13, 5
, 14, 5
, 15, 6
, 16, 6
, 17, 7
, 18, 8
, 19, 9
, 10)
"L20"."HDIF"
FROM
/* Get up to 20 of the most recent (last) games played. */
( SELECT FIRST 20 "PlayerID" "PID", "GID" "GID",
RANK ( ) OVER ( PARTITION BY "PlayerID" ORDER BY "Diff" ) "Rnk",
"Diff" "HDIF", "Date" "Gdate"
FROM "Vw_Plyr_Diff"
WHERE "PlayerID" = 1)
"L20"
) "T10"
You need to put parentheses around the expression in FIRST. As specified in the Firebird 3.0 Language Reference for FIRST, SKIP:
SELECT
[FIRST <m>] [SKIP <n>]
FROM ...
...
<m>, <n> ::=
<integer-literal>
| <query-parameter>
| (<integer-expression>)
So, use
select first (decode(...)) ....
When using subqueries directly in first, you need to use double parentheses (once for the expression, and once for the fact that sub-queries in expressions are enclosed in parentheses.
The SQL standard OFFSET/FETCH clauses introduced in Firebird 3 do not support expressions.
Beware, your current code doesn't specify an ORDER BY, this means it is undefined exactly which rows are returned, it will depend on location of data inside the database, the access plan, etc. I would recommend that you add an appropriate ORDER BY clause to ensure the returned rows are as expected.
It looks like you're trying to SELECT from the derived table L20 defined in the FROM clause, and not from an actual table L20. If you want to be able to do that, then L20 most be specified as a common table expression.

How to combine multiple condition in sql server

Table1
ID
12
21
12
21
...
Conditon
1)
I need to check either id should 12 or id should be 21. It should not be other numbers.
Below query is working
SELECT distinct ltrim(id) from table1 where ltrim(id) = '12' or ltrim(id) = '21')
2)
I dont need muliple number, always 12 or always 21, It should not be mixed, like
id
12
12
12
or
id
21
21
21
Below query is working
Declare #0_Recorddup int = 0
SELECT #0_Recorddup = Count(id) from (SELECT distinct id from table1) t1
if (#0_Recorddup = 0) or (#0_Recorddup > 1)
begin
''error message
end
How to merge a both query, can anyone help me....
ltrim(id) = '12'
You store id's - a numeric value - as a string? I am sorry, but I hope you program better.
For integers it is simple like in most other languages:
id = 12
and yes, etc. aredoable. I would suggest you grab some book about SQL and start learnng basics. Seriously.
I need to check either id should 12 or id should be 21. It should not be other numbers.
Simple. Trivial. Like in any other programming language:
id = 12 OR id = 21
alternative in SQL:
id IN (12, 21)
that is not as nice for 2 numbers but gets in handy fast.
"SQL for Dummies" (IBAM 1118607961, available through amazon etc.) is a decent book for someone at your level. Explains the basics. Like how not to compare numbers as strings.

Simple SQL Statement query using IIF

I am trying to report a simple 3 column query using MS Query from a single Table, The 3 columns are 'Sales Person [EXECNAME]', 'No of Cars Sold [ITEM]', 'No of Cars That had Finance on it' [FINANCECASES]. There is no field which simply shows YES for Finance, but the is a field with the Finance Value, so I was trying to use the IIF to simply add 1 for each record that had a FinanceValue that doesn't = 0. Here is my attempt but it is simply not working? Any help would be much appreciated.
SELECT t.EXECNAME, COUNT(t.ITEM),
IIF(SUM(t.TOTALFINANCE) = 0, 0, SUM(FINANCECASES) + 1) AS FINANCECASES
FROM t.csv t
GROUP BY t.EXECNAME
Steve
SELECT t.EXECNAME, COUNT(t.ITEM),
SUM(CASE WHEN t.TOTALFINANCE > 0 THEN 1 ELSE 0 END) AS FINANCECASES
FROM t.csv t
GROUP BY t.EXECNAME