How to add a row with null data in final query? - clickhouse

I have got a table with data:
table1
country date price
USA 2001-01-25 2
RUS 2001-01-25 17
GER 2001-01-25 30
USA 2001-02-25 11
RUS 2001-02-25 22
RUS 2001-02-26 25
I can get all countries with
SELECT DISTINCT country FROM table1;
country
USA
RUS
GER
And get all prices for the month
SELECT sum(price), country FROM table1 WHERE date >= '2001-02-01' AND date < '2001-03-01' GROUP BY country, price;
sum(price) country
11 USA
47 RUS
But I also want to see one row for country 'GER'
sum(price) country
11 USA
47 RUS
0 GER
How to do it easy in clickhouse?

Use conditional aggregation:
SELECT
country,
SUM(CASE WHEN date >= '2001-02-01' AND date < '2001-03-01'
THEN price ELSE 0 END) AS prices
FROM table1
GROUP BY country;
The problem with your current WHERE clause is that it will filter off countries which have no matching price at all during the month of February, 2001.
If the above solution be not performant, we can writing this as a join of a table containing all countries to table1:
SELECT c.country, COALESCE(t.prices, 0) AS prices
FROM (SELECT DISTINCT country FROM table1) c
LEFT JOIN
(
SELECT country, SUM(prices) AS prices
FROM table1
WHERE date >= '2001-02-01' AND date < '2001-03-01'
GROUP BY country
) t
ON c.country = t.country;

Related

Average Function with not null columns - Hive

I want to calculate an average for the first 3 years income which is not NULL for eg :
employee id 2016 2015 2014 2013 2012 2011 2010
1 100 NULL 200 50 10 50 50
average should be on 100 + 200 + 50 / 3
employee id 2016 2015 2014 2013 2012 2011 2010
2 NULL 100 NULL 50 NULL 25 100
average should be 100 + 50 + 25 / 3
Get one row per year with union all. Then rank the rows with row_number function so that non-null rows would be ranked first. Then get the average of first 3 rows.
select employee_id,avg(income)
from (select employee_id,yr,income
,row_number() over(partition by employee_id order by cast((income is not null) as int) desc,yr desc) as rnum
from (select employee_id,2016 as yr,`2016` as income from tbl
union all
select employee_id,2015 as yr,`2015` as income from tbl
union all
select employee_id,2014 as yr,`2014` as income from tbl
union all
select employee_id,2013 as yr,`2013` as income from tbl
union all
select employee_id,2012 as yr,`2012` as income from tbl
union all
select employee_id,2011 as yr,`2011` as income from tbl
union all
select employee_id,2010 as yr,`2010` as income from tbl
) t
) t
where rnum <= 3
group by employee_id
When 2 columns have values, the result would be (val1+val2)/2.
When only one column has a value, the result would be that column.
When all columns have a null value, null is returned.

Oracle SQL where clauses

I would like to select all customers (id_cust attribute) and sum of income (income attrib.) in customertable which have transaction (income) in year 2016 (incomedate attrib) but they don't have transaction (income) in 2017. So all transaction are in one table.
id_cust income incomedate
123 101 2/5/2016
123 211 6/1/2017
221 900 9/7/2017
221 300 8/9/2016
....
Sum of income per customer
select sum(income),id_cust
from customertable
group by id_cust
Sum of income per customer per year
select sum(income),id_cust,to_char(incomedate,'YYYY') year
from customertable
group by id_cust,to_char(incomedate,'YYYY')
You can try as below:
select t1.id_cust, sum(t1.income)
from customertable t1
WHERE
to_char(t1.incomedate, 'YYYY') = '2016'
and not exists (
SELECT 1
FROM customertable t2
WHERE t2.id_cust = t1.id_cust
AND to_char(t2.incomedate,'YYYY') = '2017'
)
group by t1.id_cust;
Hope it help.

One column calculate multiple output

I have show the total product sale on the basis YTD (Year to Date), QTD(Quarter to Date) and MTD (Month to Date). The thing is I have to show only one from those. Only one output can be seen on the basis of selection i.e. like we have radio buttons to select one from many. Here also a input is given to select and on the basis of that input the output is generated. The input can be any YTD,QTD or MTD. The output is generated on the basis of input. I don't how to calculate a column output where the input can be vary.
I have a Product Table-
Product_ID Product_name Price
1 Mobile 200
2 T.V. 400
3 Mixer 300
I have a Sales table like this-
Product_ID Sales_Date Quantity
1 01-01-2015 30
2 03-01-2015 40
3 06-02-2015 10
1 22-03-2015 30
2 09-04-2015 10
3 21-05-2015 40
1 04-06-2015 40
2 29-07-2015 30
1 31-08-2015 30
3 14-09-2015 30
And my ouput column contains 3 columns that are-
Product_id, Product_Name and Total_Amount.
The column Total_Amount(quantity*price) have to calculate sale on the basis of input given by user i.e.,
IF it is YTD then it should calculate the total sale from Starting Date of Year ( 01-01-2015) to the current_date(sysdate),
IF it is QTD then in which quarter the current date is falling i.e if current month is september then from 1 July to current_date(sysdate),
IF it is MTD then in which month the current date is falling to the current_date(sysdate).
Can anyone help. Thanks!!!
-- step 1
create or replace view my_admin
as
select 'YTD' element, product_id, sum(quantity) sum_quantity
from sales
where Sales_date between trunc(sysdate,'Y') and sysdate
group by product_id
union
select 'QTD', product_id, sum(quantity) sum_quantity
from sales
where Sales_date between trunc(sysdate,'Q') and sysdate
group by product_id
union
select 'MTD', product_id, sum(quantity) sum_quantity
from sales
where Sales_date between trunc(sysdate,'MM') and sysdate
group by product_id
-- step 2
select element, p.product_name, (sum_quantity * p.PRICE) agregate
from my_admin a
inner join products p on a.product_id = p.product_id
where element = (:input)
My presumption is that you have 3 radio buttons(variables :YTD,:QTD,:MTD in my example) where just one value at a time can be picked by the user the rest will be null.
You can use a something like this to get what you want:
select SUM(a.QTY*B.PRICE) from PRODUCTS a
inner join SALES B on a.PRODUCT_ID=B.PRODUCT_ID
where
(:YTD is null or B.SALES_DATE between '01-JAN-15' and sysdate)
and
(:QTD is null or TO_CHAR(B.SALES_DATE, 'YYYY-Q')=TO_CHAR(sysdate, 'YYYY-Q'))
and
(:MTD is null or TO_CHAR(B.SALES_DATE, 'MM')=TO_CHAR(sysdate, 'MM'));
You can test it here sqlfiddle

Find only particular days between two dates

I have an Oracle table with data like below:
1. ID DATE
2. 12 02/11/2013
3. 12 02/12/2013
4. 13 02/11/2013
5. 13 02/12/2013
6. 13 02/13/2013
7. 13 02/14/2013
8. 14 02/11/2013
9. 14 02/12/2013
10. 14 02/13/2013
I need to find only those ID who has only Monday, Tuesday and Wednesday dates, so here only ID = 14 should be returned. I am using Oracle and dates are in format MM/DD/YYYY.
Please advice.
Regards,
Nitin
If date column is DATE datatype, then you can
select id
from your_table
group by id
having sum(case
when to_char(date_col,'fmday')
in ('monday','tuesday','wednesday') then 1
else 99
end) = 3;
EDIT: Corected the above code at the igr's observation
But this is ok only if you don't have a day twice for the same id.
If the column is varchar2 then the condition becomes to_char(to_date(your_col,'mm/dd/yyyy'),'fmday') in ...
A more robust code would be:
select id
from(
select id, date_col
from your_table
group by id, date_col
)
group by id
having sum(case
when to_char(date_col,'fmday', 'NLS_DATE_LANGUAGE=ENGLISH')
in ('monday','tuesday','wednesday') then 1
else 99
end) = 3;
select id
from (
select
id,
sum (case when to_char(dt, 'D', 'nls_territory=AMERICA') between 1 and 3 then 1 else -1 end) AS cnt
from t
group by id
)
where cnt=3
NOTE: I assumed (id,dt) is unique - no two lines with same id and date.
do something like
SELECT * FROM your_table t
where to_char(t.DATE, 'DY') in ('whatever_day_abbreviation_day_you_use');
alternatively if you prefer you could use day numbers like:
SELECT * FROM your_table t
where to_number(to_char(d.ts, 'D')) in (1,2,3);
if you'd like to avoid ID repetition add DISTINCTION
SELECT DISTINCT ID FROM your_table t
where to_number(to_char(d.ts, 'D')) in (1,2,3);

Unique rows in oracle 11g

I have a query which returns a set of records as like the one below:-
Date Dept commission
5-Apr Sales 20
4-Apr Sales 21
1-Jan Marketing 35
case 1: If i run a query between 1 Jan and 5 april I should get
Date Dept commission
5 April Sales 76
case 2: and when I run the query between jan 1 and jan 31 should get the output as
Date Dept commission
1 Jan Marketing 35
Case 2 is simple as when i put hte date range getting the required results , but not sure how to handle case 1 to show the max / latest date , the Dept for that date and a sum of the commission for that Dept , date for the selected date range . The output will be a single row with the latest date and department with a sum(commission) for the selected date range.
SELECT
MAX(Date) AS Date
, ( SELECT tt.Dept
FROM tableX tt
WHERE tt.Date = MAX(t.Date)
) AS Dept
, SUM(Commission) AS Commission
FROM
tableX t
WHERE
Date BETWEEN StartDate AND EndDate
The above works in SQL-Server, MySQL, Postgres as the sql-fiddle, test-1 shows, however it does NOT work in Oracle 11g R2 !
This works though (sql-fiddle, test-2):
SELECT
MAX(t.Date) AS Date
, MIN(tt.Dept) AS Dept --- MIN, MAX irrelevant
, SUM(t.Commission) AS Commission
FROM
( SELECT
MAX(Date) AS Date
, SUM(Commission) AS Commission
FROM
tableX
WHERE
Date BETWEEN StartDate AND EndDate
) t
JOIN
tableX tt
ON tt.Date = t.Date
The MIN(tt.Dept) is used to take care of the case you have more than row with the maximum date, say one row with Sales and one with Marketing, both in Apr-5
This works, too, using the LAST_VALUE analytic function (sql-fiddle, test-3):
SELECT
MAX(Date) AS Date
, MIN(Dept) AS Dept
, SUM(Commission) AS Commission
FROM
( SELECT
Date AS Date
, LAST_VALUE(Dept) OVER( ORDER BY Date
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS Dept
, Commission AS Commission
FROM
tableX
WHERE
Date BETWEEN StartDate AND EndDate
) t

Resources