Summing all - / + values from a column by a specific year - sql

I have joined 2 tables. One table has all the values (+/- amounts) and the other has mainly dimensional data. Once joining, I wanted to run a query to sum of all negative and positive values given a specific year.
Problem seems to be happening on the third line. Any thoughts?
select sum(sales_amount)
from salesInfo s inner joint dimInfo d
where sales_amount <0 and year = '2019';
The query is not generating due to an error being thrown on line 3:
error - ORA-00905: missing keyword 00905. 00000 - "missing keyword"

Issue is likely due to the missing ON clause which in Oracle SQL is not allowed for INNER JOIN unlike other database dialects which treats such a join equivalent to a cross join.
Alternatively, you can use Oracle's NATURAL JOIN to join on matching named columns between tables:
from salesInfo s natural join dimInfo d
Either way, you can then run a conditional aggregate and even group by year:
select year,
sum(case when sales_amount < 0 then sales_amount end) as negative_sales,
sum(case when sales_amount > 0 then sales_amount end) as positive_sales
from salesInfo s
inner join dimInfo d on s.some_id = d.some_id
group by year
Rextester Demo

Add an ON clause after the JOIN statement to specify the JOIN condition
SELECT sum(sales_amount)
FROM salesInfo s
INNER JOIN dimInfo d
ON d.<column_name> = s.<column_name>
WHERE sales_amount < 0 and year = '2019'

Just use conditional aggregation:
select sum(case when sales_amount < 0 then sales_amount end) as neg_sum,
sum(case when sales_amount > 0 then sales_amount end) as pos_sum
from salesInfo s inner join
dimInfo d
on ? = ? -- whatever your `JOIN` conditions are here
where year = 2019;

Related

GROUP BY with flagged value

Below query is to return flag as Y if c.LAST_UPDATED_TIMESTAMP < MAX(t.LATEST_ACTION_TIMESTAMP)
SELECT
'Y' as CAN_UPDATE, t.LATEST_ACTION_TIMESTAMP
FROM CUSTOMERS c
LEFT JOIN TRANSACTIONS t ah on (c.customer_id = t.customer_id)
WHERE
t.status_active_flag = 'Y' and c.customer_ID ='CUST_019'
GROUP BY t.LATEST_ACTION_TIMESTAMP
HAVING c.LAST_UPDATED_TIMESTAMP < MAX(t.LATEST_ACTION_TIMESTAMP);
ORA-00979 not a GROUP BY expression encountered, understand that all columns in SELECT need to be included in GROUP BY clause. How can handle for the flagged value 'Y' in this case?
Column c.LAST_UPDATED_TIMESTAMP need to be added to group by part as well
GROUP BY t.LATEST_ACTION_TIMESTAMP, c.LAST_UPDATED_TIMESTAMP
here is a dbfiddle with a dumb example
The HAVING clause will effectively change the LEFT JOIN to an INNER JOIN and you can, since you are taking the maximum of the column you are grouping by and aggregating on that column is irrelevant as there will only be a singular value per group, then move the HAVING comparison to the ON clause of the join without aggregation:
SELECT 'Y' as CAN_UPDATE,
t.LATEST_ACTION_TIMESTAMP
FROM CUSTOMERS c
INNER JOIN TRANSACTIONS t
ON ( c.customer_id = t.customer_id
AND c.LAST_UPDATED_TIMESTAMP < t.LATEST_ACTION_TIMESTAMP
)
WHERE t.status_active_flag = 'Y'
AND c.customer_id ='CUST_019'
GROUP BY t.LATEST_ACTION_TIMESTAMP;
You could also use a DISTINCT (or UNIQUE) clause instead of the GROUP BY clause.

Join 2 tables on multiple case conditions

I am using pgAdmin on a Postgres db. I am trying to achieve the following result (amounts are random):
In order to do that, I need to query the 2 tables: accounts and transactions
I am not sure how to get the sum(amount) results into 1 column. I have tried the following:
select SUM(
CASE WHEN debit_account_id = 1 then amount
when credit_account_id = 1 then amount * (-1) else 0 end),
SUM(
CASE WHEN debit_account_id = 2 then amount
when credit_account_id = 2 then amount * (-1) else 0 end)
from transactions
where entity_id = 1
and so on up to account_id 6. This will give me the correct sums for each account but each result is in new column. How I can combine this so the results looks like in example above?
You can use UNION ALL.
select debit_account_id account_id, -amount from transactions
union all
select credit_account_id account_id, amount from transactions;
now you have data together in one column
I'd sum the debits and the credits for each account in different queries and join them on the accounts table:
SELECT account_name, sum_credut - sum_debit AS balance
FROM accounts a
JOIN (SELECT credit_account_id, SUM(amount)
FROM transfer
GROUP BY credit_account_id) c ON a.id = c.credit_account_id
JOIN (SELECT debit_account_id, SUM(amount)
FROM transfer
GROUP BY debit_account_id) d ON a.id = d.debit_account_id
I would recommend a lateral joins for this:
select a.account_name,
sum(v.signed_amount) as total_amount
from transactions t left join lateral
(values (t.debit_account_id, t.amount),
(t.credit_account_id, - t.amount)
) v(account_id, signed_amount) join
account a
on a.id = v.account_id
group by a.account_name;
I don't see entity_id in any of the tables, so I don't know where that comes from.

Combining Two Queries with Similar Actions to One

I have a query below that gives me the total number of hours in a year that a person walks greater than 2 mph Unioned with a query on the total amount of entries (total number of hours recorded). Both are basically the same query with the exception of the last clause in the first one. The issue is that it takes a good 30 seconds to run this query. Is there a way for me to combine the two queries to make it run faster but get similar data? My end goal is to get the percentage of time a person walks greater than 2 mph.
SELECT COUNT(STARTING_HOUR) FROM SENSOR.SPEED
FULL OUTER JOIN ACCOUNT.ID
ON SENSOR.SPEED.Account_ID = ACCOUNT.ID.Account_ID
FULL OUTER JOIN ACCOUNT.NAME
ON ACCOUNT.ID.Account_ID = ACCOUNT.NAME.Account_ID
WHERE UPPER(NAME) LIKE '%Sarah%'
AND UPPER(NAME) LIKE '%Jones%'
AND STARTING_HOUR >= TO_DATE('2015-01-01T00:00:00', 'YYYY-MM-
DD"T"HH24:MI:SS')
AND STARTING_HOUR <= TO_DATE('2015-12-31T00:00:00', 'YYYY-MM-
DD"T"HH24:MI:SS')
AND Value > 2
UNION
SELECT COUNT(STARTING_HOUR) FROM SENSOR.SPEED
FULL OUTER JOIN ACCOUNT.ID
ON SENSOR.SPEED.Account_ID = ACCOUNT.ID.Account_ID
FULL OUTER JOIN ACCOUNT.NAME
ON ACCOUNT.ID.Account_ID = ACCOUNT.NAME.Account_ID
WHERE UPPER(NAME) LIKE '%Sarah%'
AND UPPER(NAME) LIKE '%Jones%'
AND STARTING_HOUR >= TO_DATE('2015-01-01T00:00:00', 'YYYY-MM-DD"T"HH24:MI:SS')
AND STARTING_HOUR <= TO_DATE('2015-12-31T00:00:00', 'YYYY-MM-DD"T"HH24:MI:SS')
Thank you!
First, full outer join is totally superfluous. Then table aliases make the query easier to write and read. And then you can do the arithmetic using AVG():
SELECT AVG(CASE WHEN VALUE > 2 THEN 1.0 ELSE 0 END)
FROM SENSOR.SPEED s JOIN
ACCOUNT.ID i
ON s.Account_ID = i.Account_ID JOIN
ACCOUNT.NAME n
ON i.Account_ID = n.Account_ID
WHERE UPPER(NAME) LIKE '%Sarah%' AND
UPPER(NAME) LIKE '%Jones%' AND
STARTING_HOUR >= DATE '2015-01-01' AND
STARTING_HOUR <= DATE '2015-12-31' ;
I'm pretty sure the WHERE clauses turn all the outer joins into inner joins. Perhaps you do want an outer join somewhere, but it is not obvious that any are necessary.
SELECT CASE
WHEN COUNT(1) = 0 -- Handle division by zero
THEN NULL
ELSE COUNT( CASE WHEN value > 2 THEN 1 END )
/ COUNT( 1 )
END AS Percentage
FROM SENSOR.SPEED
RIGHT OUTER JOIN ACCOUNT.NAME
ON SENSOR.SPEED.Account_ID = ACCOUNT.NAME.Account_ID
WHERE UPPER(NAME) LIKE '%SARAH%'
AND UPPER(NAME) LIKE '%JONES%'
AND STARTING_HOUR BETWEEN DATE '2015-01-01' AND DATE '2015-12-31'
Do you need the ACCOUNT.ID table? Instead, could you join directly from SENSOR.SPEED to ACCOUNT.NAME?
I am assuming that NAME is in ACCOUNT.NAME and with the UPPER(NAME) filter this will never be NULL so you can do a RIGHT OUTER JOIN instead of a FULL OUTER JOIN. Depending on which table the STARTING_HOUR column is in, this could be further simplified to an INNER JOIN.

SQL join and count from different tables

Basically i have two tables one being doctor the second being appointments, i want to count the appointments made for each doctor but also include any doctors in the list which didn't have any appointments so far i have come up with this.
SELECT DISTINCT doctor.doctor_id
, sum(case when appt_date > 0 then 1 else 0 end) AppointmentCount
FROM appointment,doctor JOIN doctor d
WHERE appointment.doctor_id = d.doctor_id
group by doctor_id;
this prints out each doctor id but makes it so the count for each doctor is exactly the same whereas i want them to have different values based on how many appointments have been made.
Any idea how to fix this?
Select d.doctor_id, count(a.app_date) as count
from doctor d left join appointment a on (d.doctor_id = a.doctor_id)
group by d.doctor_id;
You can check the demo here: SQLFiddle
Remove distinct keyword (note that you are using group by). Avoid using Join in where clause; Try using on.
Left Join shall give you the expected result.
Your query will be:
SELECT d.doctor_id,
sum(case when a.appt_date > 0 then 1 else 0 end) AppointmentCount
FROM doctor d
left join appointment a
on d.doctor_id = a.doctor_id
group by d.doctor_id;
You don't need to use distinct: GROUP BY is already deduplicating the entries (because it's grouping by doctor_id.
The easiest way to do this is to use left join:
select d.doctor_id
, count(a.appt_date) as appointmentCount
from doctor as d
left join appointment as a on d.doctor_id = a.doctor_id
group by d.doctor_id
select doctor.doctor_id, nvl(count(appointment),0)
from doctor, appointment
where doctor.doctor_id = appointment.doctor_id(+)
group by doctor.doctor_id
outer join on appointment with doctor_id, group by doctor_id. use NVL in case no appointments for doctor so you can display zero

SQL LEFT JOIN finding non-zero value as zero

I have the following query with many LEFT JOIN clauses that has 7 result columns, the last two of which are numbers. I'm expecting the count_non_zero column to always be equal to the count_total column (given the data I current have)
WITH temp_table AS (
SELECT
attr.company_name_id AS option_id,
attr.company_name AS option_name,
uj.internship_company_name_id,
AVG(CASE WHEN s.salary > 0 THEN s.salary END) AS average,
COUNT(CASE WHEN s.salary > 0 THEN attr.company_name END) as count_non_zero,
COUNT(attr.company_name_id) as count_total
FROM company_name attr
LEFT JOIN user_job_internship uj ON uj.internship_company_name_id = attr.company_name_id
AND attr.approved_by_administrator = 1
LEFT JOIN salary_internship s ON uj.user_job_internship_id = s.user_job_id
AND uj.job_type_id = 4
LEFT JOIN [user] u ON u.user_id = uj.user_id AND u.include_in_student_site_results = 1
AND u.site_instance_id IN (1)
LEFT JOIN user_education_mba_school mba ON u.user_id = mba.user_id
AND mba.mba_graduation_year_id NOT IN (8)
GROUP BY attr.company_name_id, attr.company_name, uj.internship_company_name_id)
SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY average DESC) AS row, *
FROM temp_table WHERE count_total >= 3) sub
WHERE row >= 1 AND row <= 25 ORDER BY average DESC;
I run this query to prove that no values in the 'salary' column are returning a value of 0.
SELECT s.* FROM user_job_internship uj, salary_internship s
where internship_company_name_id = 440
AND uj.user_job_internship_id = s.user_job_id
I'm thinking there is something that messes up the results that is causing the count_non_zero to get counts that do not exist. Anyone have anythoughts?
I am assuming your count_total is greater than your count_non_zero. That is to be expected because you are using outer join to join user_job_internship and salary_internship.
Your query is including companies that do not have any internships. A company will not be included in the count_non_zero if either the salary is 0 or if there is no internship at all.
Change those two joins to inner joins and you should get your expected result.
The other option is to change your count_total to ignore companies that haven't any internship
count(case when s.user_job_id is not null then attr.company_name_id end) as count_total
You have one other slight risk. Your count_non_zero is counting company_name whereas your count_total is counting company_name_id. You could have problems if the company_name column allows NULL values.