Combining three similar sql queries into a single result - sql

I have a table say (TimeValue) something like this
Time Value Owner
======================
1 10 A
2 20 B
3 30 C
4 40 A
5 50 B
6 60 C
7 70 A
8 80 B
Now I have three sql statement like the followings
select owner, value as 'First sql' from TimeValue where time >= 1 and time <= 3 group by owner
select owner, value as 'Second sql' from TimeValue where time >= 4 and time <= 6 group by owner
select owner, value as 'Third sql' from TimeValue where time >= 7 and time <= 9 group by owner
Now these are the result of the above sql
1.
Value Owner
=================
10 A
20 B
30 C
2.
Value Owner
=================
40 A
50 B
60 C
3
Value Owner
===============
70 A
80 B
90 C
My question, what would be the sql statement if I want the following result??
Owner First SQL Second SQL Third SQL
==================================================
A 10 40 70
B 20 50 80
C 30 60 90
Thank you in advance

One way is to use a CASE statement, like this:
SELECT
owner
, MAX(CASE WHEN time >= 1 AND time <= 3 THEN value ELSE NULL END) AS FIRST
, MAX(CASE WHEN time >= 4 AND time <= 6 THEN value ELSE NULL END) AS SECOND
, MAX(CASE WHEN time >= 7 AND time <= 9 THEN value ELSE NULL END) AS THIRD
FROM TimeValue
GROUP BY owner
Note that you need an aggregating function, such as MAX, around value. In fact, many RDBMS engines would not even take SQL without an aggregating function around it.

SQL FIDDLE
select owner,
SUM(case when (time >= 1 and time <= 3) then value end) as 'FirstSQL',
SUM(case when (time >= 4 and time <= 6) then value end) as 'SecondSQL',
SUM(case when (time >= 7 and time <= 9) then value end) as 'ThirdSQL'
from TimeValue
group by owner
base on your example you should use SUM instead of MAX

The same idea with dasblinkenlight but different approach and I agree on aggregating function, such as MAX or SUM
SELECT
owner,
MAX(IF(time between 1 and 3, value,NULL) ) as first,
MAX(IF(time between 4 and 6, value,NULL) ) as sec,
MAX(IF(time between 7 and 9, value,NULL) ) as third
FROM TimeValue
GROUP BY owner

you can also use self-joins:
select
tv0.owner
, tv0.value sql1
, tv1.value sql2
, tv2.value sql3
from timevalue tv0
inner join timevalue tv1 on tv1.owner = tv0.owner
inner join timevalue tv2 on tv2.owner = tv0.owner
where tv0.time between 1 and 3
and tv1.time between 4 and 6
and tv2.time between 7 and 9

Related

Subtract in Union

I have this data, where I want to generate the last row "on the fly" from the first two:
Group
1yr
2yrs
3yrs
date
code
Port
19
-15
88
1/1/2020
arp
Bench
10
-13
66
1/1/2020
arb
Diff
9
2
22
I am trying to subtract the Port & Bench returns and have the difference on the new row. How can I do this?
Here's my code so far:
Select
date
Group,
Code,
1 yr returnp,
2 yrs returnp,
3yrs return
From timetable
union
Select
date,
Group,
Code,
1 yr returnb,
2 yrs returnb,
3yrs returnb
From timetable
Seems to me that a UNION ALL in concert with a conditional aggregation should do the trick
Note the sum() is wrapped in an abs() to match desired results
Select *
From YourTable
Union All
Select [Group] = 'Diff'
,[1yr] = abs(sum([1yr] * case when [Group]='Bench' then -1 else 1 end))
,[2yrs] = abs(sum([2yrs] * case when [Group]='Bench' then -1 else 1 end))
,[3yrs] = abs(sum([3yrs] * case when [Group]='Bench' then -1 else 1 end))
,[date] = null
,[code] = null
from YourTable
Results
Group 1yr 2yrs 3yrs date code
Port 19 -15 88 2020-01-01 arp
Bench 10 -13 66 2020-01-01 arb
Diff 9 2 22 NULL NULL
If you know there is always 2 rows, something like this would work
SELECT * FROM timetable
UNION ALL
SELECT
MAX(1yr) - MIN(1yr),
MAX(2yrs) - MIN(2yrs),
MAX(3yrs) - MIN(3yrs),
null,
null,
FROM timetable

Select max of nested id from amazon redshift

My database is an amazon redshift.
I have a table that looks like this -
id
nested_id
date
value
1
10
'2021-01-01'
5
1
20
'2021-01-01'
10
1
10
'2021-01-02'
6
1
20
'2021-01-02'
11
1
10
'2021-01-03'
7
1
20
'2021-01-03'
12
2
30
'2021-01-01'
5
2
40
'2021-01-01'
10
2
30
'2021-01-02'
6
2
40
'2021-01-02'
11
2
30
'2021-01-03'
7
2
40
'2021-01-03'
12
So this is basically a table that tracks values by id over time, except for every id there can be a nested_id. And the dates and values are primarily connected to the nested_id.
However, let's say I'm starting with the id field, but for each id I want to only return the points over time for the nested_id that has the greater sum of points.
So right now I'm just grabbing it like this...
select *
from mytable
where id in (1, 2)
except I only want it to return nested_id rows where the maximum value of that nested_id is the greatest.
So here's how I would do this manually.
For id of 1, the maximum value is 12, and the nested_id of that value is 20
For id of 2, the maximum value is 12, and the nested_id of that value is 40
So my return table should be
id
nested_id
date
value
1
20
'2021-01-01'
10
1
20
'2021-01-02'
11
1
20
'2021-01-03'
12
2
40
'2021-01-01'
10
2
40
'2021-01-02'
11
2
40
'2021-01-03'
12
Is there an easy way of performing this query? I'm assuming you have to partition somehow?
You can solve this with row_number window functions
with maxs as (
select id,
nested_id,
value,
row_number() over (partition by id order by value desc) rn
from mytable
)
select mt.*
from mytable mt
left join maxs on mt.id = maxs.id and mt.nested_id = maxs.nested_id
where maxs.rn = 1

SQL query return only unique results

I have the following SQL query which is working correctly...
SELECT *
FROM
wp_rg_lead
INNER JOIN wp_rg_lead_detail ON
wp_rg_lead.id=wp_rg_lead_detail.lead_id
WHERE wp_rg_lead.form_id = '7'
and cast(date_created as date) >= current_date - interval '7' day
I would like to modify this to only return unique records. The problem I have is that each 'entry' is actually made up of several like so....
id | form_id | entry_id | value
--------------------------------------------
1 1 75 red
2 1 75 broken
3 1 75 apple
4 7 33 yellow
5 7 33 faulty
6 7 33 banana
7 7 33 ripe
8 7 22 red
9 7 22 strawberry
10 7 22 broken
11 7 22 squashed
Using the above data I would only want it to return 2 results
Does this do what you want?
SELECT distinct form_id
FROM wp_rg_lead INNER JOIN
wp_rg_lead_detail
ON wp_rg_lead.id = wp_rg_lead_detail.lead_id
WHERE cast(date_created as date) >= current_date - interval '7' day;
If you want an entire arbitrary row, it looks like you are using Postgres, so you can use distinct on:
SELECT distinct on (form_id) *
FROM wp_rg_lead INNER JOIN
wp_rg_lead_detail
ON wp_rg_lead.id = wp_rg_lead_detail.lead_id
WHERE cast(date_created as date) >= current_date - interval '7' day
ORDER BY form_id, id;

Oracle select sum by time window

Lets assume that we have the ORACLE table of the following format and data:
TIMESTAMP MESSAGENO ORGMESSAGE
------------------------- ---------------------- -------------------------------------
27.04.13 1 START PERIOD
27.04.13 3 10
27.04.13 4 5
28.04.13 5 6
28.04.13 3 20
29.04.13 4 25
29.04.13 5 26
30.04.13 2 END PERIOD
30.04.13 1 START PERIOD
01.05.13 3 10
02.05.13 4 15
02.05.13 5 16
03.05.13 3 30
03.05.13 4 35
04.05.13 5 36
05.05.13 2 END PERIOD
I want to select sum of all the ORGMESSAGE for all the period (window between START PERIOD and END PERIOD) grouped by MESSAGENO.
Exapmle output would be:
PERIOD START PERIOD END MESSAGENO SUM
------------ ------------- -------- ----
27.04.13 30.04.13 3 25
27.04.13 30.04.13 4 30
27.04.13 30.04.13 5 32
30.04.13 05.05.13 3 45
30.04.13 05.05.13 4 50
30.04.13 05.05.13 5 52
I am guessing that use of ORACLE Analityc function woulde be suitable but really dont know how and where to start.
Thanks in advance for any help.
If we assume that the period starts and ends match, then a simple way to find the matching messages is to count the preceding number of starts. This is a cumulative sum and it is easy in Oracle. The rest is just aggregation:
select min(timestamp) as periodstart, max(timestamp) as periodend, messageno, count(*)
from (select om.*,
sum(case when messageno = 1 then 1 else 0 end) over (order by timestamp) as grp
from orgmessages om
) om
where messageno not in (1, 2)
group by grp, messageno;
Note that this method (as with the others) really wants the timestamp to be unique on each record. In the data presented, these solutions will work. But if you have multiple starts and ends on the same day, none of them will work assuming that timestamp only has the date.
First find all period ends per period start. Then join with your table to group and sum.
select
dates.start_date,
dates.end_date,
messageno,
sum(to_number(orgmessage)) as period_sum
from mytable
join
(
select start_dates.timestmp as start_date, min(end_dates.timestmp) as end_date
from (select * from mytable where orgmessage = 'START PERIOD') start_dates
join (select * from mytable where orgmessage = 'END PERIOD') end_dates
on start_dates.timestmp < end_dates.timestmp
group by start_dates.timestmp
) dates on mytable.timestmp between dates.start_date and dates.end_date
where mytable.orgmessage not like '%PERIOD%'
group by dates.start_date, dates.end_date, messageno
order by dates.start_date, dates.end_date, messageno;
SQL fiddle: http://www.sqlfiddle.com/#!4/365de/15.
please, try this one, replace rrr with your table name
select periodstart, periodend, messageno, sum(to_number(orgmessage)) s
from (select TIMESTAMP periodstart,
(select min (TIMESTAMP) from rrr r2 where orgmessage = 'END PERIOD' and r2.TIMESTAMP > r.TIMESTAMP) periodend
from rrr r
where orgmessage = 'START PERIOD'
) borders, rrr r
where r.TIMESTAMP between borders.periodstart and borders.periodend
and r.orgmessage not in ('END PERIOD', 'START PERIOD')
group by periodstart, periodend, messageno
order by periodstart, periodend, messageno

How to show different dates data (from the same table) as columns in Oracle

I'm sorry if the title wasn't too clear, but the following explanation will be more accurate.
I have the following view:
DATE USER CONDITION
20140101 1 A
20140101 2 B
20140101 3 C
20140108 1 C
20140108 3 B
20140108 2 C
What I need to do is present how many users where in all conditions this week and 7 days before today.
Output should be like this:
Condition Today Last_Week (Today-7)
A 0 1
B 1 1
C 2 1
How can I do this in Oracle? I will need to do this for 4 weeks so itll be Today-7,14-21.
I've tried this with group by but I get the "week2" as rows. Then I've tried something like Select conditions, (select count(users) from MyView where DATE='Today') FROM MyView(looking at something thats actually working) but it doesnt work for me.
Achieved this with a little modification of the accepted answer:
select condition,
count(case when to_date(xdate) = to_date(sysdate) then 1 end) to_day,
count(case when to_date(xdate) = to_date(sysdate-7) then 1 end) last_7_days
from my_table
group by condition
select condition, count(case when to_date(xdate) = to_date(sysdate) then 1 end) to_day,
count(case when to_date(xdate) < to_date(sysdate) then 1 end) last_7_days
from my_table
where to_date(xdate) >= to_date(sysdate) - 7
group by condition
select condition
, sum
( case
when date between trunc(sysdate) - 7 and trunc(sysdate) - 1
then 1
else 0
end
)
last_week
, sum
( case
when date between trunc(sysdate) and trunc(sysdate + 1)
then 1
else 0
end
)
this_week
from table
group
by condition
By using the conditional count (as a sum) and grouping on condition you can filter out all desired dates. Note that using trunc will cause to use the begin of the day.