SAP HANA | With Clause performance - hana

We are using SAP HANA 1.0 SPS12.
We have daywise table like below -
select trans_date,article,measure1,measure2 from table_1
Volume of table ~ 5 millions rows
we need to see data like -
select 'day-1',sum(measure1),sum(meaure2) from table1 where trans_date=add_days(current_date,-1) group by 'day-1'
union all
select 'day-2',sum(measure1),sum(meaure2) from table1 where trans_date>=add_days(current_date,-2) group by 'day-2'
union all
select 'WTD',sum(measure1),sum(meaure2) from table1 where trans_date>=add_days(current_date,-7) group by 'WTD'
union all
select 'WTD-1',sum(measure1),sum(meaure2) from table1 where trans_date>=add_days(current_date,-15) and trans_Date <= add_days(current_date,-7) group by 'WTD-1'
so on and so forth for MTD,MTD-1,MTD-2,YTD.
Performance wise is it better to use WITH CLAUSE and hold data for one year and then split according to timeframe? OR Is it better to use separate aggregation for each timeframe as shown above.
As far as I understand, in RDBMSs like Oracle, WITH CLAUSE materializes results and use it from the memory. SAP HANA is In Memory database itself. Does using WITH CLAUSE in SAP HANA gives distinctive performance edge?
Query using WITH CLAUSE -
WITH t1 as
(
select trans_date,sum(measure1),sum(meaure2) from table1 where trans_date>=add_days(current_date,-365)
)
select 'day-1',sum(measure1),sum(meaure2) from t1 where trans_date=add_days(current_date,-1) group by 'day-1'
union all
select 'day-2',sum(measure1),sum(meaure2) from t1 where trans_date>=add_days(current_date,-2) group by 'day-2'
union all
select 'WTD',sum(measure1),sum(meaure2) from t1 where trans_date>=add_days(current_date,-7) group by 'WTD'
union all
select 'WTD-1',sum(measure1),sum(meaure2) from t1 where trans_date>=add_days(current_date,-15)
and trans_Date <= add_days(current_date,-7)
group by 'WTD-1'

If you care about performance, putting the data in a single row should be much better:
select sum(case when trans_date = add_days(current_date, -1) then measure1 end) as measure1_day1,
sum(case when trans_date = add_days(current_date, -1) then measure2 end) as measure2_day1,
sum(case when trans_date = add_days(current_date, -2) then measure1 end) as measure1_day2,
sum(case when trans_date = add_days(current_date, -2) then measure2 end) as measure2_day2,
. . .
from table1
where trans_date >= add_days(current_date, -15);
You can unpivot the results afterwards, if you really need the values in separate rows.
Alternatively, you can do:
select days, sum(measure1), sum(measure2)
from (select 1 as days from dummy union all
select 2 from dummy union all
select 7 from dummy union all
select 15 from dummy
) d left join
table1 t
on t.trans_date = add_days(current_date, - d.days)
group by days
order by days;

Related

when joining two tables using union how to use order by clause for two tables seperatly in sql

i am creating two tables with same type of attributes i have to join these tables using union in such a way that the resultant union set should have 1st table in ascending order and 2nd table in descending order
I think you want something like this:
select t.*
from ((select t1.*, 1 as which
from t1
) union all
(select t2.*, 2 as which
from t2
)
) t
order by which,
(case when which = 1 then col end) asc,
(case when which = 2 then col end) desc;

How to define unique value in union if two rows are not same data

I'm creating a simple SQL query with union, the result is returned correctly, but how to set a default value in a dummy column if the union result has two rows for one value?
If the result returned two values for one employee, then the dummy column is 'N' for the first value and 'Y' for the second value.
And if the result returned only one value for the employee, then the dummy column is 'Y'
How to achieve that?
This is the query that I'm using
select
dbo.employee,
dbo.starting_date
from
table_1
union
select
dbo.employee,
dbo.hiring_date
from
table_2
With a CTE:
with cte as (
select dbo.employee, dbo.starting_date date from table_1
union all
select dbo.employee, dbo.hiring_date date from table_2
)
select
t.*,
case when exists (
select 1 from cte
where employee = t.employee and date > t.date
) then 'N' else 'Y' end dummycolumn
from cte t
You can use window functions for this:
select t.employee, t.date,
(case when 1 = row_number() over (partition by t.employee order by t.date)
then 'Y' else 'N'
end) as dummy
from ((select t1.employee, t1.starting_date as date
from table_1 t1
) union all
(select t2.employee, t2.starting_date as date
from table_2 t2
)
) t

Insert two select queries in a single row

I have two select statements, both having some common fields.
This is my table structure :
Id, year, valueA, ValueB
Here is my two select statements
SELECT
id, [year],
SUM(Total_Volume) / (count(distinct(month)) * 7)
FROM
TableA
GROUP BY
id, [year]
and
SELECT
id, [year],
SUM(Total_volume) / (count(distinct(month)) * 5)
FROM
TableA
WHERE
weekday NOT IN ('Friday','Saturday')
GROUP BY
station_id, [year]
I have two different conditions for two statements. Id and year is common for both the statements.
Result from first select statement should be stored in column valueA and result from second select statement should be stored in ValueB .
Any possible way to combine these queries and insert them into table as a single statement?
SELECT a.id ,a.[year],valA,valB
from(
SELECT id ,[year],SUM(Total_Volume)/ (count(distinct(month))*7) valA from TableA
group by id,[year]) a INNER JOIN
(select id,[year],SUM(Total_volume)/(count(distinct(month))*5) valB
from TableA
WHERE weekday NOT IN ('Friday','Saturday')
group by station_id,[year]) b
on a.id=b.id and a.[year]=b.[year]
You could do both the calculation in a single query
SELECT id,
[year],
Sum(total_volume) / ( Count(DISTINCT( Month)) * 7 ),
Sum(CASE
WHEN weekday NOT IN ('Friday', 'Saturday' ) THEN
total_volume
ELSE 0
END) / (Count(DISTINCT( Month)) * 5 )
FROM tableA
GROUP BY id,
[year];

refer to value of unnest (or other srf) in WHERE

While I'm trying to find good answer on my question Analog of OUTER APPLY in other RDBMS (not SQL Server) I've found pretty nice PostgreSQL solution:
create table Transactions
(
ID int, Date timestamp, Amount decimal(29, 2), Amount2 decimal(29, 2)
);
insert into Transactions (ID, Date, Amount, Amount2)
select 1, current_timestamp, 100.00, null union all
select 2, current_timestamp, 25.00, 75.00;
select
T.ID,
T.Date,
unnest(array[T.Amount, T.Amount2]) as Amount
from Transactions as T
SQL FIDDLE
the point is to turn some columns into rows with most readable and elegant code I could get. But I don't want to see null columns as rows. Is there any way I could use value from unnest in WHERE clause of the query?
You can use a subquery and where to filter out the NULL values:
select id, date, Amount
from (select t.*, unnest(array[T.Amount, T.Amount2]) as Amount
from Transactions as T
) t
where Amount is not null;
Postgres doesn't allow the unnest direction in the where clause.
EDIT:
Unnest uses the length of the array to determine the number of rows. You can do this with standard SQL and no subquery, but you will probably find it messier:
select T.ID, T.Date,
(case when n = 1 then T.Amount
when n = 2 then T.Amount2
end) as Amount
from Transactions T cross join
(select 1 as n union all select 2) n
where (case when n = 1 then T.Amount
when n = 2 then T.Amount2
end) is not null;

multiple select in one query [Teradata]

I'm trying to do multiple select from diff tables and just have a result in one column.
SELECT COUNT(*) FROM tb1 union
SELECT COUNT(*) FROM tb2 union
SELECT COUNT(*) FROM tb3;
output should be like:
593643
18103600
0
Problem with this is that the result is being arranged on desc order.
Like below:
0
593643
18103600
I would want the result to be as I put the select statement.
Please advise. Btw, I'm using teradata.
Thank you.
SQL result sets are inherently unordered, unless you explicitly specify an order by clause. You can do this with a subquery:
select cnt
from ((SELECT COUNT(*) as cnt, 1 as ord FROM tb1)
union all
(SELECT COUNT(*), 2 FROM tb2)
union all
(SELECT COUNT(*), 3 FROM tb3)
) t
order by ord
If you want specific order, add ORDER BY clause. It would also be good to use UNION ALL so you always get 3 rows, even with duplicate results (two tables having the same number of rows):
SELECT 'tbl1' AS tablename, COUNT(*) AS cnt, 1 AS ord FROM tb1 UNION ALL
SELECT 'tbl2', COUNT(*), 2 FROM tb2 UNION ALL
SELECT 'tbl3', COUNT(*), 3 FROM tb3
ORDER BY ord ;