Coalescing values in a column over a partition - sql

I have chosen to ask this question via an example as I think it most clearly illustrates what I am trying to do.
Say I have the following table:
member number time
------ ----- -----
1 2 19:21
1 4 19:24
1 27 19:37
2 4 19:01
2 7 21:56
2 8 22:00
2 21 22:01
How can I obtain the following column?
member number new column
------ ----- ---------
1 2 2.4.27
1 4 2.4.27
1 27 2.4.27
2 4 4.7.8.21
2 7 4.7.8.21
2 8 4.7.8.21
2 21 4.7.8.21
EDIT(S):
I am using DB2 SQL.
There is not necessarily the same number of rows for each member.
The order is determined by time say.

depending on your version of db2, the LISTAGG() function may be available to you. i think it is included in any db2 version after 9.7.
example:
select
member,
number,
listagg(number,',') as new_column
from
tablename
group by
member

In Oracle this will do the job,
select a.member,a.number,b.newcol from table a,(select member,replace(wm_concat(number),',','.') newcol from test11 group by member)b where a.member=b.member;

I know it's bad form answering your own question but I have found this useful page:
https://www.ibm.com/developerworks/mydeveloperworks/blogs/SQLTips4DB2LUW/entry/aggregating_strings42?lang=en
Modifying the code there gives:
create table test (member int, number int, time_stamp time)`;
insert into test values
(1,2,'19:21'),
(1,4,'19:24'),
(1,27,'19:37'),
(2,4,'19:01'),
(2,7,'21:56'),
(2,8,'22:00'),
(2,21,'22:01');
select
member, substr(xmlcast(xmlgroup('.' || number as a order by time_stamp) as varchar(60)), 2)
from test
group by member

Related

Join two rows that contain a common column value [duplicate]

Let's say I've got the following database table
Name | Nickname | ID
----------------------
Joe Joey 14
Joe null 14
Now I want to do a select statement that merges these two columns to one while replacing the null values. The result should look like this:
Joe, Joey, 14
Which sql statement manages this (if it's even possible)?
Simplest solution:
SQL> select * from t69
2 /
NAME NICKNAME ID
---------- ---------- ----------
Joe Joey 14
Joe 14
Michael 15
Mick 15
Mickey 15
SQL> select max(name) as name
2 , max(nickname) as nickname
3 , id
4 from t69
5 group by id
6 /
NAME NICKNAME ID
---------- ---------- ----------
Joe Joey 14
Michael Mickey 15
SQL>
If you have 11gR2 you could use the new-fangled LISTAGG() function but otherwise it is simple enough to wrap the above statement in a SELECT which concatenates the NAME and NICKNAME columns.
AFAIK,
the question is not clear.so i am making some assumptions over here.
your output has the first and 3rd columns for both the rows as same.
Only the 2nd field is different.
so u can simply write a select quest
select one.name,two.nick_name,one.id from
(select name,id from your_tb group by name,id) one,
your_tb two
where two.nickname is not NULL
and two.name=one.name
and two.id=one.id;
may be we can tune this but i am not good in tuning sql squeries,but this is the way i suppose u need.

Solving Logical Questions Using SQL

I am trying to solve a problem for a fun work exercise showing that SQL can be used to solve it. It is a puzzle that goes as follows:
Successfully navigating the waters during sea voyages is a challenging task. A captain’s most important decision is selecting the right crew for the voyage. A mix of different skill sets are required to sail the ship efficiently, navigate to the destination, and fish for food along the way.
Table 1 shows a list of crew members that are available for you to hire for the voyage. Each crew member demands a salary for the voyage and has different skill levels of Fishing, Sailing, and Navigation.
In order for your journey to be successful, you must have a cumulative skill of 15 or more in each of the three skill categories from all of your chosen crew members. You may choose as many crew members as you like.
Question: What is the minimum achievable cost for the voyage?"
I would say I am what I would consider an intermediate to advanced (depending on the situation) SQL user.
Not asking for an answer per-say but I have thought about the best way to solve and I was first thinking using a WHILE loop in some way. I have create a table to hold the data and added a 'salary_ranking' column (below). I am curious if anyone has any tips or suggestions on routes to go? I would like to use something I have never used before but also am trying to get to the most efficient answer.
Here is the data (I added the last column):
NAME FISHING SAILING NAVIGATION SALARY SALARY_RANK
---------- ----------- ----------- ----------- ----------- -----------
Amy 3 5 1 46000 3
Bill 1 2 5 43000 2
Carl 3 4 2 47000 4
Dan 4 3 1 36000 1
Eva 4 2 2 43000 2
Fred 1 3 4 55000 5
Greg 3 1 5 68000 8
Henry 5 4 2 64000 7
Ida 3 3 3 60000 6
(9 rows affected)
This is a CTE version, where I first create test data, then run a recursive query, using a MaxID to prevent it doing all the permutations.
declare #t table(Id int, NAME varchar(10), FISHING int, SAILING int, NAVIGATION int, SALARY int)
insert #t values (1,'Amy',3,5,1,46000)
,(2,'Bill',1,2,5,43000 )
,(3,'Carl',3,4,2,47000)
,(4,'Dan',4,3,1,36000)
,(5,'Eva',4,2,2,43000)
,(6,'Fred',1,3,4,55000)
,(7,'Greg',3,1,5,68000)
,(8,'Henry',5,4,2,64000)
,(9,'Ida',3,3,3,60000 )
;with cte as (
select convert(varchar(1000),name) as crew, fishing, sailing, navigation, salary, ID as MaxID from #t
union all
select convert(varchar(1000),cte.crew+', '+ t.name), cte.fishing+t.fishing, cte.sailing+t.sailing, cte.navigation+t.navigation, cte.salary+t.salary, t.ID
from #t t
join cte on t.ID>cte.MaxID
)
select top 1 crew,fishing,sailing,navigation,salary
from cte
where fishing>=15 and sailing>=15 and navigation>=15
order by salary
result is:
crew fishing sailing navigation salary
Amy, Bill, Carl, Greg, Henry 15 16 15 268000

SQL field default "count(another_field) +1"

I need to create a field COUNT whose default value is the automatically generated count of times NAME has appeared in that table till now, as shown in example below. Since i am adding the field to an existing table, i also need to populate existing rows. How best to go about this please?
ID NAME COUNT
1 peter 1
2 jane 1
3 peter 2
4 peter 3
5 frank 1
6 jane 2
7 peter 4
You would do this when you are querying the table, using the ANSI-standard row-number function:
select id, name, row_number() over (partition by name order by id) as seqnum
from t;

Grouping a concrete number of rows in a SQL query

I want to group data without any specific criteria, just the number of data for each resultant group. I have a table like this:
DATE VAL1 VAL2
------------ ------ ------
01-01-2013 5 8
01-02-2013 14 23
01-03-2013 10 6
01-04-2013 21 88
01-05-2013 9 11
01-06-2013 4 9
01-07-2013 19 42
01-08-2013 8 4
01-09-2013 12 1
01-10-2013 2 8
01-11-2013 31 65
01-12-2013 3 6
...
Think that date field could be, for example, a number and not a date...
What I want is, for example, get the total sum or average of groups of data, where groups have a specific number of rows (the same number for all groups).
For example, for three rows per group, where I want get the total sum of VAL1 and the average of VAL2:
INTERVAL SUM VAL 1 AVG VAL 2
----------------------- --------- ---------
01-01-2013 - 01-03-2013 29 12.3
01-04-2013 - 01-06-2013 34 33.3
01-07-2013 - 01-09-2013 39 15.6
01-10-2013 - 01-12-2013 36 26.3
...
I really think it's possible to do with a query, but I can't find the way to get the proper "group by" sentence. Can somebody help me?
Thanks a lot in advance!
You can use row_number function divided by 3 to assign unique number to each group of 3 consecutive rows. Then, you can aggregate on this group number.
select min("DATE") ||'-'||max("DATE"),
sum(val1),
avg(val2)
from (
select "DATE", val1, val2,
ceil(row_number() over (order by "date") / 3) as grp
from mytab
) as x
group by grp
order by grp;
Please try:
select
min("DATE")||' - '||max("DATE") "Interval",
sum(Val1) "SUM VAL 1",
cast(avg(Val2) as numeric(18,1)) "AVG VAL 2"
from(
select
"DATE",
ceil(extract(month from "DATE")/3) dt,
Val1,
Val2
from
YourTable
)x
group by dt
order by dt
SQL Fiddle Demo
The ROWNUM pseudo column in oracle or the LIMIT of mysql could help you acheive it.
I think what you mean is pagination. Given in this link.
http://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56asktom-086197.html

Merging two rows to one while replacing null values

Let's say I've got the following database table
Name | Nickname | ID
----------------------
Joe Joey 14
Joe null 14
Now I want to do a select statement that merges these two columns to one while replacing the null values. The result should look like this:
Joe, Joey, 14
Which sql statement manages this (if it's even possible)?
Simplest solution:
SQL> select * from t69
2 /
NAME NICKNAME ID
---------- ---------- ----------
Joe Joey 14
Joe 14
Michael 15
Mick 15
Mickey 15
SQL> select max(name) as name
2 , max(nickname) as nickname
3 , id
4 from t69
5 group by id
6 /
NAME NICKNAME ID
---------- ---------- ----------
Joe Joey 14
Michael Mickey 15
SQL>
If you have 11gR2 you could use the new-fangled LISTAGG() function but otherwise it is simple enough to wrap the above statement in a SELECT which concatenates the NAME and NICKNAME columns.
AFAIK,
the question is not clear.so i am making some assumptions over here.
your output has the first and 3rd columns for both the rows as same.
Only the 2nd field is different.
so u can simply write a select quest
select one.name,two.nick_name,one.id from
(select name,id from your_tb group by name,id) one,
your_tb two
where two.nickname is not NULL
and two.name=one.name
and two.id=one.id;
may be we can tune this but i am not good in tuning sql squeries,but this is the way i suppose u need.