Selecting common values and adding them SQL - sql

I have a table called Test. It has three columns Fn(varchar),Un(number),INS(date). What I am trying to do is select all rowsfrom the table and get the sum of common values in column Fn.
Example
Fn Un INS
---------------
1 6 9-Jan-2017
2 8 9-Jan-2017
1 5 4-Jan-2017`
I am trying to sum common values based on Fn column so the result would be
Fn Un
1 11
I am trying to understand how to CASE IF ELSE in Oracle. But not able to add them because the INS date is different for both of them. Is there any way to achieve this. Thanks.

I think you need group by like below:
select fn, sum(un) as total
from t
group by fn
order by total desc;
You can use Aggregate functions(Average, Sum, Max, Min..) in sql with group by.
https://en.wikipedia.org/wiki/Aggregate_function

You seem to want group by:
select fn, sum(un)
from t
group by fn
having count(*) >= 2;

Try LISTAGG function for INS column
SELECT Fn, SUM(Un) as Total, LISTAGG(INS, '; ') WITHIN GROUP (ORDER BY Fn) as INSs
FROM T
GROUP BY Fn
ORDER BY Total DESC;

Related

Count Distinct values in one column based on other column

I am trying to count distinct values on Z_l based on value by using with clause. Sample data exercise included below.
please look at the picture, the distinct values of Z_l based on X='ny'
with distincz_l as (select ny.X, ny.z_l o.cnt From HOPL ny join (select X, count(*) as cnt from HOPL group by X) o on (ny.X = o.Z_l)) select * from HOPL;
You don't even need a WITH clause, since you just need one single sentence:
SELECT z_l, count(1)
FROM hopl
WHERE x='ny'
GROUP BY z_l
;

SQL - Group by with having Result

For the following table i need to fetch user who did min 2 distinct transactions and have sum of net sale equal to or more than 20,
But, everything need to be in same select cant use temp table, i am using the below query, but getting ambiguity in result,
select z.customer_nbr, transaction_nbr
from sales_transaction,
(select customer_nbr
from sales_transaction
group by customer_nbr
having count(transaction_nbr) >=2) z
group by z.customer_nbr, transaction_nbr
having sum(net_sales_rtl)>20
Below is the result
Result ambiguity - customer_numer have no transaction with no 16
By "user", I assume you mean the entity referred to by customer_nbr.
Your query is only looking at the net sales for a single transaction, not for the entire customer.
You seem to want aggregation and having:
select st.customer_nbr
from sales_transaction st
group by st.customer_nbr
having count(distinct st.transaction_nbr) >= 2 and
sum(st.net_sales) > 20;
If you wanted all transactions to follow the 20 minimum, then two levels of aggregation would be appropriate:
select ct.customer_nbr
from (select st.customer_nbr, st.transaction_nbr,
sum(st.net_sales) as transaction_net_sales
from sales_transaction st
group by st.customer_nbr, st.transaction_nbr
) ct
group by ct.customer_nbr
having count(*) >= 2 and
min(ct.transaction_net_sales) > 20;
I think what is missing here is a join between the results from sales_transaction and the subquery z.
Considering both your tables share column transaction_nbr, you could have something like this:
select z.customer_nbr, s.transaction_nbr
from sales_transaction s,
(select customer_nbr, transaction_nbr
from sales_transaction
group by customer_nbr, transaction_nbr
having count(transaction_nbr) >=2) z
where z.transaction_nbr = s.transaction_nbr
group by z.customer_nbr, transaction_nbr
having sum(net_sales_rtl)>20

Group rows with similar strings

I have searched a lot, but most of solutions are for concatenation option and not what I really want.
I have a table called X (in a Postgres database):
anm_id anm_category anm_sales
1 a_dog 100
2 b_dog 50
3 c_dog 60
4 a_cat 70
5 b_cat 80
6 c_cat 40
I want to get total sales by grouping 'a_dog', 'b_dog', 'c_dog' as dogs and 'a_cat', 'b_cat', 'c_cat' as cats.
I cannot change the data in the table as it is an external data base from which I am supposed to get information only.
How to do this using an SQL query? It does not need to be specific to Postgres.
Use case statement to group the animals of same categories together
SELECT CASE
WHEN anm_category LIKE '%dog' THEN 'Dogs'
WHEN anm_category LIKE '%cat' THEN 'cats'
ELSE 'Others'
END AS Animals_category,
Sum(anm_sales) AS total_sales
FROM yourtables
GROUP BY CASE
WHEN anm_category LIKE '%dog' THEN 'Dogs'
WHEN anm_category LIKE '%cat' THEN 'cats'
ELSE 'Others'
END
Also this query should work with most of the databases.
By using PostgreSQL's split_part()
select animal||'s' animal_cat,count(*) total_sales,sum(anm_sales) sales_sum from(
select split_part(anm_cat,'_',2) animal,anm_sales from x
)t
group by animal
sqlfiddle
By creating split_str() in MySQL
select animal||'s' animal_cat,count(*) total_sales,sum(anm_sales) sales_sum from(
select split_str(anm_cat,'_',2) animal,anm_sales from x
)t
group by animal
sqlfiddle
You could group by a substr of anm_catogery:
SELECT SUBSTR(anm_catogery, 3) || 's', COUNT(*)
FROM x
GROUP BY anm_catogery
If you have a constant length of the appendix like in the example:
SELECT CASE right(anm_category, 3) AS animal_type -- 3 last char
, sum(anm_sales) AS total_sales
FROM x
GROUP BY 1;
You don't need a CASE statement at all, but if you use one, make it a "simple" CASE:
Simplify nested case when statement
Use a positional reference instead of repeating a possibly lengthy expression.
If the length varies, but there is always a single underscore like in the example:
SELECT split_part(anm_category, '_', 2) AS animal_type -- word after "_"
, sum(anm_sales) AS total_sales
FROM x
GROUP BY 1;

SQL query count divided by a distinct count of same query

Having some trouble with some SQL.
Take the following result for instance:
LOC_CODE CHANNEL
------------ --------------------
3ATEST-01 CHAN2
3ATEST-01 CHAN3
3ATEST-02 CHAN4
What I need to do is get a count of the above query, grouped by channel, but i want that count to be divided by the count that the "LOC_CODE" appears.
Example of the result I am after is:
CHANNEL COUNT
---------------- ----------
CHAN2 0.5
CHAN3 0.5
CHAN4 1
Above explaination is that the CHAN2 appears next to "3ATEST-01", but that LOC_CODE of "3ATEST-01" appears twice, so the count should be divided by 2.
I know I can do this by basically duplicating the query with a distinct count, but the underlying query is quite complex and don't really want to harm performance.
Please let me know if you would like more information!
Try:
select channel,
count(*) over (partition by channel, loc_code)
/ count(*) over (partition by loc_code) as count_ratio
from my_table
SELECT t.CHANNEL, COUNT(*) / gr.TotalCount
FROM my_table t JOIN (
SELECT LOC_CODE, COUNT(*) TotalCount
FROM my_table
GROUP BY LOC_CODE
) gr USING(LOC_CODE)
GROUP BY t.LOC_CODE, t.CHANNEL
Create a index on (LOC_CODE, CHANNEL)
If are no duplicate channels, replace COUNT(*) / gr.TotalCount with 1 / gr.TotalCount and remove the GROUP BY clause
First, find a query that gets you the correct results. Then, see if it can be optimised. My guess is that it's hard to optimise as you require two different groupings, one per Channel and one pre Loc_Code.
I'm not even sure that this fits your description:
SELECT t.CHANNEL
, COUNT(*) / SUM(grp.TotalCount)
FROM my_table t
JOIN
( SELECT LOC_CODE
, COUNT(*) TotalCount --- or is it perhaps?:
--- COUNT(DISTINCT CHANNEL)
FROM my_table
GROUP BY LOC_CODE
) grp
ON grp.LOC_CODE = t.LOC_CODE
GROUP BY t.CHANNEL
Your requirements are still a bit unclear to me when it comes to duplicate CHANNELs, but this should work if you want grouping on both CHANNEL and LOC_CODE to sum up later;
SELECT L1.CHANNEL, 1/COUNT(L2.LOC_CODE)
FROM Locations L1
LEFT JOIN Locations L2 ON L1.LOC_CODE = L2.LOC_CODE
GROUP BY L1.CHANNEL, L1.LOC_CODE
Demo here.

Switch case in aggregate query

I want to have a switch case in my SQL query such that when the group by does not group any element i dont want to aggregate otherwise I want to. Is that possible.
my query is something like this:
select count(1),AVG(student_mark) ,case when Count(1)=1 then student_subjectid else null end from Students
group by student_id
i get this error Column 'student_subjectid' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Thanks in advance..
SELECT
student_id,
COUNT(*) AS MarkCount,
AVG(student_mark) AS student_mark,
CASE COUNT(*) WHEN 1 THEN MIN(student_subjectid) END AS student_subjectid
FROM Students
GROUP BY student_id
Why in the world would you complicate it?
select count(1), AVG(Student_mark) Student_mark
from Students
group by student_id
If there is only one student_mark, it is also the SUM, AVG, MIN and MAX - so just continue to use the aggregate!
EDIT
The dataset that would eventuate with your requirement will not normally make sense. The way to achieve that would be to merge (union) two different results
select
numRecords,
Student_mark,
case when numRecords = 1 then student_subjectid end # else is implicitly NULL
from
(
select
count(1) AS numRecords,
AVG(Student_mark) Student_mark,
min(student_subjectid) as student_subjectid
from Students
group by student_id
) x