How to take sum of all columns in hive - sql

Is is possible to take sum of all columns in hive table. I mean any single way to take sum
Table
col col_1 col_2 col_3
Ouptut
sum(col), sum(col_1), sum(col_2) sum(col_3)

create table mytable (i int,j int,k int);
insert into mytable values (1,2,3),(4,5,6),(7,8,9);
select pos+1 as col
,sum (val) as sum_col
from mytable t
lateral view posexplode(array(*)) pe
group by pos
;
+-----+---------+
| col | sum_col |
+-----+---------+
| 1 | 12 |
| 2 | 15 |
| 3 | 18 |
+-----+---------+
Or (So help me god)
select map_values
(
str_to_map
(
concat_ws
(
','
,sort_array
(
collect_list
(
concat_ws
(
':'
,lpad(cast(pos as string),10,'0')
,cast(sum_val as string)
)
)
)
)
)
) as sum_col_array
from (select pos
,sum (val) as sum_val
from mytable t
lateral view posexplode(array(*)) pe
group by pos
) t
;
+------------------+
| sum_col_array |
+------------------+
| ["12","15","18"] |
+------------------+

Related

transform columns to rows

I have a table table1 like below
+----+------+------+------+------+------+
| id | loc | val1 | val2 | val3 | val4 |
+----+------+------+------+------+------+
| 1 | loc1 | 10 | 190 | null | 20 |
| 2 | loc2 | 20 | null | 10 | 10 |
+----+------+------+------+------+------+
need to combine the val1 to val4 into a new column val with a row for each so that the output is like below.
NOTE: - I data I have has val1 to val30 -> ie. 30 columns per row that need to be converted into rows.
+----+------+--------+
| id | loc | val |
+----+------+--------+
| 1 | loc1 | 10 |
| 1 | loc1 | 190 |
| 1 | loc1 | null |
| 1 | loc1 | 20 |
| 2 | loc2 | 20 |
| 2 | loc2 | null |
| 2 | loc2 | 10 |
| 2 | loc2 | 10 |
+----+------+--------+
You can use lateral join for transform columns to rows :
SELECT a.id,a.loc,t.vals
FROM table1 a,
unnest(ARRAY[a.val1,a.val2,a.val3,a.val4]) t(vals);
If you want to this with a dynamic added columns:
CREATE OR REPLACE FUNCTION columns_to_rows(
out id integer,
out loc text,
out vals integer
)
RETURNS SETOF record AS
$body$
DECLARE
columns_to_rows text;
BEGIN
SELECT string_agg('a.'||attname, ',') into columns_to_rows
FROM pg_attribute
WHERE attrelid = 'your_table'::regclass AND --table name
attnum > 0 and --get just the visible columns
attname <> all (array [ 'id', 'loc' ]) AND --exclude some columns
NOT attisdropped ; --column is not dropped
RETURN QUERY
EXECUTE format('SELECT a.id,a.loc,t.vals
FROM your_table a,
unnest(ARRAY[%s]) t(vals)',columns_to_rows);
end;
$body$
LANGUAGE 'plpgsql'
Look at this link for more detail: Columns to rows
You could use a cross join with generate_series for this:
select
id,
loc,
case x.i
when 1 then val1
when 2 then val2
. . .
end as val
from t
cross join generate_series(1, 4) x (i)
It uses the table only once and can be easily extended to accommodate more columns.
Demo
Note: In the accepted answer, first approach reads the table many times (as many times as column to be unpivoted) and second approach is wrong as there is no UNPIVOT in postgresql.
I'm sure there's a classier approach than this.
SELECT * FROM (
select id, loc, val1 as val from #t a
UNION ALL
select id, loc, val2 as val from #t a
UNION ALL
select id, loc, val3 as val from #t a
UNION ALL
select id, loc, val4 as val from #t a
) x
order by ID
Here's my attempt with unpivot but cant get the nulls, perhaps perform a join for the nulls? Anyway i'll still try
SELECT *
FROM (
SELECT * FROM #t
) main
UNPIVOT (
new_val
FOR val IN (val1, val2, val3, val4)
) unpiv
It will not work in postgress as needed by user. Saw when it was mentioned in comments.
I am finding a way to handle "NULL"
select p.id,p.loc,CASE WHEN p.val=0 THEN NULL ELSE p.val END AS val
from
(
SELECT id,loc,ISNULL(val1,0) AS val1,ISNULL(val2,0) AS val2,ISNULL(val3,0) AS val3,ISNULL(val4,0) AS val4
FROM Table1
)T
unpivot
(
val
for locval in(val1,val2,val3,val4)
)p
Test
EDIT:
Best Solution from my Side:
select a.id,a.loc,ex.val
from (select 'val1' as [over] union all select 'val2' union all select 'val3'
union all select 'val1' ) pmu
cross join (select id,loc from Table1) as a
left join
Table1 pt
unpivot
(
[val]
for [over] in (val1, val2, val3, val4)
) ex
on pmu.[over] = ex.[over] and
a.id = ex.id
Test

Column to rows, rows to rows in SQL Server

I have table like this
id | vname1 | vname2 | vname3
1 | vala | valb | valc
I want this to convert like this
id | vname | vals
1 | vname1 | vala
1 | vname2 | valb
1 | vname3 | valc
I thought about pivoting but here I think is not the case
Do a UNION ALL, with one SELECT for each vname column:
select id, 'vname1' as vname, vname1 as vals from tablename
union all
select id, 'vname2' as vname, vname2 as vals from tablename
union all
select id, 'vname3' as vname, vname3 as vals from tablename
You can use the UNPIVOT function to convert the columns into rows:
Sample Example:
select Id,
indicatorname,
from yourtable
unpivot
(
indicatorvalue
for indicatorname in (Indicator1, Indicator2, Indicator3)
) unpiv;
Link for reference: UnPivot

SQL: Pick highest and lowest value (int) from one row

I am looking for a way to pick the highest and lowest value (integer) from a single row in table. There are 4 columns that i need to compare together and get highest and lowest number there is.
The table looks something like this...
id | name | col_to_compare1 | col_to_compare2 | col_to_compare3 | col_to_compare4
1 | John | 5 | 5 | 2 | 1
2 | Peter | 3 | 2 | 4 | 1
3 | Josh | 3 | 5 | 1 | 3
Can you help me, please? Thanks!
You can do this using CROSS APPLY and the VALUES clause. Use VALUES to group all your compared columns and then select the max.
SELECT
MAX(d.data1) as MaxOfColumns
,MIN(d.data1) as MinOfColumns
,a.id
,a.name
FROM YOURTABLE as a
CROSS APPLY (
VALUES(a.col_to_compare1)
,(a.col_to_compare2)
,(a. col_to_compare3)
,(a.col_to_compare4)
,(a. col_to_compare5)
) as d(data1) --Name the Column
GROUP BY a.id
,a.name
Assuming you are looking for min/max per row
Declare #YourTable table (id int,name varchar(50),col_to_compare1 int,col_to_compare2 int,col_to_compare3 int,col_to_compare4 int)
Insert Into #YourTable values
(1,'John',5,5,2,1),
(2,'Peter',3,2,4,1),
(3,'Josh',3,5,1,3)
Select A.ID
,A.Name
,MinVal = min(B.N)
,MaxVal = max(B.N)
From #YourTable A
Cross Apply (Select N From (values(a.col_to_compare1),(a.col_to_compare2),(a.col_to_compare3),(a.col_to_compare4)) N(N) ) B
Group By A.ID,A.Name
Returns
ID Name MinVal MaxVal
1 John 1 5
3 Josh 1 5
2 Peter 1 4
These solutions keep the current rows and add additional columns of min/max.
select *
from t cross apply
(select min(col) as min_col
,max(col) as max_col
from (
values
(t.col_to_compare1)
,(t.col_to_compare2)
,(t.col_to_compare3)
,(t.col_to_compare4)
) c(col)
) c
OR
select *
,cast ('' as xml).value ('min ((sql:column("t.col_to_compare1"),sql:column("t.col_to_compare2"),sql:column("t.col_to_compare3"),sql:column("t.col_to_compare4")))','int') as min_col
,cast ('' as xml).value ('max ((sql:column("t.col_to_compare1"),sql:column("t.col_to_compare2"),sql:column("t.col_to_compare3"),sql:column("t.col_to_compare4")))','int') as max_col
from t
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| id | name | col_to_compare1 | col_to_compare2 | col_to_compare3 | col_to_compare4 | min_col | max_col |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 1 | John | 5 | 5 | 2 | 1 | 1 | 5 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 2 | Peter | 3 | 2 | 4 | 1 | 1 | 4 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 3 | Josh | 3 | 5 | 1 | 3 | 1 | 5 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
A way to do this is to "break" apart the data
declare #table table (id int, name varchar(10), col1 int, col2 int, col3 int, col4 int)
insert into #table values (1 , 'John' , 5 , 5 , 2 , 1)
insert into #table values (2 , 'Peter' , 3 , 2 , 4 , 1)
insert into #table values (3 , 'Josh' , 3 , 5 , 1 , 3)
;with stretch as
(
select id, col1 as col from #table
union all
select id, col2 as col from #table
union all
select id, col3 as col from #table
union all
select id, col4 as col from #table
)
select
t.id,
t.name,
agg.MinCol,
agg.MaxCol
from #table t
inner join
(
select
id, min(col) as MinCol, max(col) as MaxCol
from stretch
group by id
) agg
on t.id = agg.id
Seems simple enough
SELECT min(col1), max(col1), min(col2), max(col2), min(col3), max(col3), min(col4), max(col4) FROM table
Gives you the Min and Max for each column.
Following OP's comment, I believe he may be looking for a min/max grouped by the person being queried against.
So that would be:
SELECT name, min(col1), max(col1), min(col2), max(col2), min(col3), max(col3), min(col4), max(col4) FROM table GROUP BY name

In the following PLSQL trigger, , why is everything being casted into AS VARCHAR2 in the pivot code?

I am trying to wrap my head around this trigger. I have trigger code here, that I'm studying:
INSERT INTO ONLINE_PROFILE_HISTORY.ONLINE_ACCOUNT_AVH
SELECT ONLINE_PROFILE_HISTORY.ONLINE_ACCOUNT_AVH_SEQ.NEXTVAL
,tmpVar
,O.ATTR_NM
,O.ATTR_OLD_VLU
,CAST(NULL AS CHAR) AS ATTR_NEW_VLU
FROM (SELECT :OLD.ONL_ACCT_ID
,CAST(:OLD.ONL_ACCT_INTL_ID AS VARCHAR2(1024)) ONL_ACCT_INTL_ID
,CAST(:OLD.ONL_ACCT_EXT_ID AS VARCHAR2(1024)) AS ONL_ACCT_EXT_ID
,CAST(:OLD.LCKED_TS AS VARCHAR2(1024)) AS LCKED_TS
,CAST(:OLD.DISABLED_TS AS VARCHAR2(1024)) AS DISABLED_TS
,CAST(:OLD.LST_SCSFL_LOGIN_TS AS VARCHAR2(1024)) AS LST_SCSFL_LOGIN_TS
/* etc etc*/
,CAST(:OLD.VLDT_SCRT_QUES_FAILURE_CNT AS VARCHAR2(1024)) AS VLDT_SCRT_QUES_FAILURE_CNT
,CAST(:OLD.VLDT_SCRT_QUES_LST_FAILURE_TS AS VARCHAR2(1024)) AS VLDT_SCRT_QUES_LST_FAILURE_TS
,CAST(:OLD.VLDT_SCRT_QUES_SUCCESS_TS AS VARCHAR2(1024)) AS VLDT_SCRT_QUES_SUCCESS_TS
,CAST(:OLD.DLTD_TS AS VARCHAR2(1024)) AS DLTD_TS
,CAST(:OLD.TRMS_CONDS_ACPTED_HOST_ADDR_NM AS VARCHAR2(1024)) AS TRMS_CONDS_ACPTED_HOST_ADDR_NM
FROM dual)
UNPIVOT (ATTR_OLD_VLU FOR
ATTR_NM IN (
ONL_ACCT_INTL_ID
,ONL_ACCT_EXT_ID
,LCKED_TS
,DISABLED_TS
,VLDT_TOKEN_VLU
,VLDT_TOKEN_KEY_NM
,VLDTD_TS
,VLDT_TOKEN_CREATE_TS
,PRIM_BILLING_ACCT_SRC_ID
,PRIM_BILLING_ACCT_SRC_ID_2
,PRIM_BILLING_ACCT_SRC_SYS_CD
,TRMS_CONDS_VER_NUM
,TRMS_CONDS_ACPTED_TS
,PSWD_FMT_NM
,VLDT_SCRT_QUES_FAILURE_CNT
,VLDT_SCRT_QUES_LST_FAILURE_TS
,VLDT_SCRT_QUES_SUCCESS_TS
,DLTD_TS
,TRMS_CONDS_ACPTED_HOST_ADDR_NM
)) O;
Why is it that we are doing the
CAST(:OLD.VLDTD_TS AS VARCHAR2(1024)) AS VLDTD_TS
part there? I generally understand how the pivot is working, but not sure why every column is casted like that.
UNPIVOT transforms the data that is spread across several columns into a single column. If the datatype of those columns in not same, it will result in error.
ORA-01790: expression must have same datatype as corresponding expression
SQL Fiddle
create table mytable(
id_ number,
col1_ varchar2(10),
col2_ number,
col3_ date
);
insert into mytable values(1, 'asd',32,date'2014-03-04');
insert into mytable values(2, 'qwe',16,date'2014-02-11');
select *
from mytable
unpivot(val_ for col_name in (
col1_,col2_,col3_)
);
ORA-01790: expression must have same datatype as corresponding expression : select * from mytable unpivot(val_ for col_name in ( col1_,col2_,col3_) )
That's why you should convert all the columns to same datatype before unpivoting.
SQL Fiddle
select *
from (
select
id_,
col1_,
to_char(col2_) col2_,
to_char(col3_,'dd-mm-yyyy') col3_
from mytable
)
unpivot(
val_ for col_name_ in (
col1_, col2_, col3_
)
)
Results:
| ID_ | COL_NAME_ | VAL_ |
|-----|-----------|------------|
| 1 | COL1_ | asd |
| 1 | COL2_ | 32 |
| 1 | COL3_ | 04-03-2014 |
| 2 | COL1_ | qwe |
| 2 | COL2_ | 16 |
| 2 | COL3_ | 11-02-2014 |

SQL rank/dense_rank and how to query/calculate with the result

So I have a table where it dense_ranks my rows.
Here is the table:
COL1 | COL2 | COL3 | DENSE_RANK |
a | b | c | 1 |
a | s | r | 1 |
a | w | f | 1 |
b | b | c | 2 |
c | f | r | 3 |
c | q | d | 3 |
So now I want to select any rows where the rank was only represented once, so the 2 is all alone, but not the 1 or 3. I want to select all the rows where this occurs, but how do I do that?
Some ideas:
-COUNT DISTINCT (RANK())
-COUNT RANK()
but neither of those are working, any ideas? please and thank you!
happy hacking
actual code:
SELECT events.event_type AS "event",
DENSE_RANK() OVER (ORDER BY bw_user_event.pad_id) as rank
FROM user_event
WHERE (software_events.software_id = '8' OR software_events.software_id = '14')
AND (software_events.event_type = 'install')
WITH Dense_ranked_table as (
-- Your select query that generates the table with dense ranks
)
SELECT DENSE_RANK
FROM Dense_ranked_table
GROUP BY DENSE_RANK
HAVING COUNT(DENSE_RANK) = 1;
I don't have SQL Server to test this. So please let me know whether this works or not.
I would think you can add a COUNT(*) OVER (PARTITION BY XXXXX) where XXXXX is what you include in your dense rank.
Then wrap this in a Common Table Expression and select where your new Count is = 1.
Something like this fiddler:
http://sqlfiddle.com/#!6/ae774/1
Code included here as well:
CREATE TABLE T
(
COL1 CHAR,
COL2 CHAR,
COL3 CHAR
);
INSERT INTO T
VALUES
('a','b','c'),
('a','s','r'),
('a','w','f'),
('b','b','c'),
('c','f','r'),
('c','q','d');
WITH CTE AS (
SELECT COL1 ,
COL2 ,
COL3,
DENSE_RANK() OVER (ORDER BY COL1) AS DR,
COUNT(*) OVER (PARTITION BY COL1) AS C
FROM dbo.T AS t
)
SELECT COL1, COL2, COL3, DR
FROM CTE
WHERE C = 1
Would return just the
b, b, c, 2
row from your test data.