Unpivoting data fails due to type conflict - sql-server-2008-r2

I am using SQL server 2008 and I am trying to unpivot the data. Here is the data that I am using:
TIME_ELAPSE BUILDINGS_CLEARED FLOORS_CLEARED AWAITING_PERC
----------- ----------------- -------------- ------------------
15h 21m 4/4 12/12 0
I need output like row values to column:
TIME_ELAPSE 15h 21m
BUILDINGS_CLEARED 4/4
FLOORS_CLEARED 12/12
AWAITING_PERC 0
What I tried:
SELECT NAME,VALUE
FROM #TEMP_DRILLUP_SUMMARY
UNPIVOT(VALUE FOR NAME IN (Time_Elapse,BUILDINGS_CLEARED,FLOORS_CLEARED,AWAITING_PERC))D
The error I received:
Msg 8167, Level 16, State 1, Line 72 The type of column "BUILDINGS_CLEARED" conflicts with the type of other columns specified in the UNPIVOT list.

Bringing all columns to the same data type will allow you to use the UNPIVOT statement.
Sample data
create table test2
(
TIME_ELAPSE nvarchar(10),
BUILDINGS_CLEARED nvarchar(10),
FLOORS_CLEARED nvarchar(10),
AWAITING_PERC int -- different type!
);
insert into test2 (TIME_ELAPSE, BUILDINGS_CLEARED, FLOORS_CLEARED, AWAITING_PERC) values
('15h 21m', '4/4', '12/12', '0');
Issue reproduction
SELECT NAME, VALUE
FROM test2
UNPIVOT(VALUE FOR NAME IN (Time_Elapse, BUILDINGS_CLEARED, FLOORS_CLEARED, AWAITING_PERC)) D
This gives:
The type of column "AWAITING_PERC" conflicts with the type of other columns specified in the UNPIVOT list.
Solution
Bring all columns to the same data type before unpivoting (I used nvarchar(10) because you did not include your table definition).
with cte as
(
select TIME_ELAPSE,
BUILDINGS_CLEARED,
FLOORS_CLEARED,
convert(nvarchar(10), AWAITING_PERC) as AWAITING_PERC -- align data types
from test2
)
SELECT NAME, VALUE
FROM cte
UNPIVOT(VALUE FOR NAME IN (TIME_ELAPSE, BUILDINGS_CLEARED, FLOORS_CLEARED, AWAITING_PERC)) D
Result
NAME VALUE
----------------- -------
TIME_ELAPSE 15h 21m
BUILDINGS_CLEARED 4/4
FLOORS_CLEARED 12/12
AWAITING_PERC 0
Fiddle to see things in action.

Related

Get distinct rows based on one column with T-SQL

I have a column in the following format:
Time Value
17:27 2
17:27 3
I want to get the distinct rows based on one column: Time. So my expected result would be one result. Either 17:27 3 or 17:27 3.
Distinct
T-SQL uses distinct on multiple columns instead of one. Distinct would return two rows since the combinations of Time and Value are unique (see below).
select distinct [Time], * from SAPQMDATA
would return
Time Value
17:27 2
17:27 3
instead of
Time Value
17:27 2
Group by
Also group by does not appear to work
select * from table group by [Time]
Will result in:
Column 'Value' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Questions
How can I select all unique 'Time' columns without taking into account other columns provided in a select query?
How can I remove duplicate entries?
This is where ROW_NUMBER will be your best friend. Using this as your sample data...
time value
-------------------- -----------
17:27 2
17:27 3
11:36 9
15:14 5
15:14 6
.. below are two solutions with that you can copy/paste/run.
DECLARE #youtable TABLE ([time] VARCHAR(20), [value] INT);
INSERT #youtable VALUES ('17:27',2),('17:27',3),('11:36',9),('15:14',5),('15:14',6);
-- The most elegant way solve this
SELECT TOP (1) WITH TIES t.[time], t.[value]
FROM #youtable AS t
ORDER BY ROW_NUMBER() OVER (PARTITION BY t.[time] ORDER BY (SELECT NULL));
-- A more efficient way solve this
SELECT t.[time], t.[value]
FROM
(
SELECT t.[time], t.[value], ROW_NUMBER() OVER (PARTITION BY t.[time] ORDER BY (SELECT NULL)) AS RN
FROM #youtable AS t
) AS t
WHERE t.RN = 1;
Each returns:
time value
-------------------- -----------
11:36 9
15:14 5
17:27 2

How to apply a function to an entire column?

I have the following table from a JDBC connection in Q.
q)r
some_int this created_at updated_at ..
-----------------------------------------------------------------------------..
1231231 "ASD" 2016.02.11D14:16:29.743260000 2016.02.11D14:16:29...
13312 "TSM" 2016.02.11D14:16:29.743260000 2016.02.11D14:16:29...
I would like to apply the following function to the first column.
deviation:{a:avg x; sqrt avg (x*x)-a*a}
This works for arrays.
q)l
1 2 3 4
q)deviation l
1.118034
How can I apply deviation on a column in a table? It seems my approach does not work:
q)select deviation(some_id) from r
'rank
UPDATE:
I cannot explain the following:
q)select avg(some_int) from r
some_int
---------
1005341
q)select min(some_int) from r
some_int
---------
812361
q)select max(some_int) from r
some_int
---------
1184014
q)select sum(some_int) from r
some_int
---------
You need to enlist the result if it is an atom since table columns must be lists, not atoms. Normally kdb can do this for you but often not when you're performing your own custom aggregations. For example, even if you define a function sum2 to be an exact copy of sum:
q)sum2:sum
kdb can only recognise sum as an aggregation and will enlist automatically, but not for sum2
q)select sum col1 from ([]col1:1 2 3 4)
col1
----
10
q)select sum2 col1 from ([]col1:1 2 3 4)
'rank
So you need to enlist in the second case:
q)select enlist sum2 col1 from ([]col1:1 2 3 4)
col1
----
10
UPDATE:
To answer your second question - it looks like your sum of numbers has spilled over the boundary for an integer. You'd need to convert them to long and then sum
q)select sum col1 from ([]col1:2147483645 1i)
col1
----------
2147483646
Above is the maximum integer. Adding one more gives infinity for an int
q)select sum col1 from ([]col1:2147483645 1 1i)
col1
----
0W
Adding anything more than that shows a blank (null)
q)select sum col1 from ([]col1:2147483645 1 1 1i)
col1
----
Solution is to cast to long before summing (or make them long in the first place)
q)select sum `long$col1 from ([]col1:2147483645 1 1 1i)
col1
----------
2147483648
You get a rank because the function does not return a list. Since the function returns a single number presumably you just want the single number answer? In which case you can simple index into the table (or use exec) to get the column vector and apply it:
deviation t`some_id
Else if you want to retain a table as the answer if you enlist the result:
select enlist deviation some_id from t

Postgres function with select

I want to display as a function this:
select LoanId, BookId ,(OutDate-InDate)*0.3 as money, OutDate-InDate days
from loans
where (OutDate-InDate)> 30 and OutDate::varchar(4)='2000';
As a input there is only year, as a output should return select.
I tried make function, but it doesn't work.
Any Idea?
I've a table like below
create table loan(loanid int,bookid int,outdate date,indate date);
and some data like this
insert into loan values(1,2,'01-12-2015','01-10-2015'),(1,2,'01-01-2016','08-01-2016');
and I use following select query
select loanid,bookid,(outdate-indate)*0.3 as "money",outdate-indate as "days"
from loan
where (OutDate-InDate)> 30 and extract(year from outdate)='2015'
loanid bookid money days
------ ------ ----- ----
1 2 18.3 61
Now I've a function to do the job that is
create or replace function FN_LOAN_DET(in_year text)
returns table (loanid int,bookid int,money numeric,days int)
as
$$
select loanid,bookid,(outdate-indate)*0.3 as "money",outdate-indate as "days"
from loan
where (OutDate-InDate)> 30 and extract(year from outdate)=$1
$$
language sql
when I call select * from FN_LOAN_DET('2015') I'll get
loanid bookid money days
------ ------ ----- ----
1 2 18.3 61

kdb: dynamically denormalize a table (convert key values to column names)

I have a table like this:
q)t:([sym:(`EURUSD`EURUSD`AUDUSD`AUDUSD);server:(`S01`S02`S01`S02)];volume:(20;10;30;50))
q)t
sym server| volume
-------------| ------
EURUSD S01 | 20
EURUSD S02 | 10
AUDUSD S01 | 30
AUDUSD S02 | 50
I need to de-normalize it to display the data nicely. The resulting table should look like this:
sym | S01 S02
------| -------
EURUSD| 20 10
AUDUSD| 30 50
How do I dynamically convert the original table using distinct values from server column as column names for the new table?
Thanks!
Basically you want 'pivot' table. Following page has a very good solution for your problem:
http://code.kx.com/q/cookbook/pivoting-tables/
Here are the commands to get the required table:
q) P:asc exec distinct server from t
q) exec P#(server!volume) by sym:sym from t
One tricky thing around pivoting a table is - the keys of the dictionary should be of type symbol otherwise it won't generate the pivot table structure.
E.g. In the following table, we have a column dt with type as date.
t:([sym:(`EURUSD`EURUSD`AUDUSD`AUDUSD);dt:(0 1 0 1+.z.d)];volume:(20;10;30;50))
Now if we want to pivot it with columns as dates , it will generate a structure like :
q)P:asc exec distinct dt from t
q)exec P#(dt!volume) by sym:sym from t
(`s#flip (enlist `sym)!enlist `s#`AUDUSD`EURUSD)!((`s#2018.06.22 2018.06.23)!30j, 50j;(`s#2018.06.22 2018.06.23)!20j, 10j)
To get the dates as the columns , the dt column has to be typecasted to symbol :
show P:asc exec distinct `$string date from t
`s#`2018.06.22`2018.06.23
q)exec P#((`$string date)!volume) by sym:sym from t
sym | 2018.06.22 2018.06.23
------| ---------------------
AUDUSD| 30 50
EURUSD| 20 10

How to select total time from three time columns (SQL Server 2008)

I have table with columns of time(0) datatypes:
Name TimeOne TimeTwo TimeThree
------ ---------------- ---------------- ----------------
Sarah 06:45:00 03:30:00 NULL
John 06:45:00 NULL NULL
How to make SELECT statement so that the "CalculatedTotal" column is total from TimeOne, TimeTwo and TimeThree per row?
What I'd like to have is select query like this:
SELECT
Name,
TimeOne,
TimeTwo,
TimeThree,
TimeOne + TimeTwo + TimeThree As CalculatedTotal
FROM
MyTable
I'd like to get back resultset like this:
Name TimeOne TimeTwo TimeThree CalculatedTotal
------ ---------------- ---------------- ---------------- ---------------
Sarah 06:45:00 03:30:00 NULL 10:15:00
John 06:45:00 NULL NULL 06:45:00
Just using plus operator in select statement gives you an error:
Operand data type time is invalid for add operator.
You could determine the number of seconds for each time value, add them together and convert back to time:
select TimeOne, TimeTwo, TimeThree,
cast(dateadd(s, isnull(datediff(s, 0, TimeOne), 0) + isnull(datediff(s, 0, TimeTwo), 0) + isnull(datediff(s, 0, TimeThree), 0), 0) as time(0)) as CalculatedTotal
from MyTable
Try the below script.
select convert(time, CONVERT(datetime, '00:08:00.000') + CONVERT(datetime, '00:07:00.000'))
select convert(time,cast('00:08:00.000'as datetime)+cast('00:07:00.000' as datetime) ) as 'T'
I belive you can use the SUM() function
SELECT
Name,
TimeOne,
TimeTwo,
TimeThree,
SUM(TimeOne+TimeTwo+TimeThree) As CalculatedTotal
FROM
MyTable
But equally you might need to convert each to seconds first using the SECOND() function, then use SEC_TO_TIME() on the result.
Edit: I've just had a look at the manual:
The SUM() and AVG() aggregate functions do not work with temporal
values. (They convert the values to numbers, which loses the part
after the first nonnumeric character.) To work around this problem,
you can convert to numeric units, perform the aggregate operation, and
convert back to a temporal value. Examples:
SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(time_col))) FROM tbl_name;
SELECT FROM_DAYS(SUM(TO_DAYS(date_col))) FROM tbl_name;