missing expression error occured in case statement - sql

I have source table Test contains id column, target table testm consist of id , col2 . Could please assist me to get rid of this error.
test (source)
id
---
10
10
20
20
20
30
30
40
Target
testm
id col2
-- ----
10 1
10 2
20 1
20 2
20 3
30 1
30 2
40 1
The query:
select id, (case id
when 10 then select count(id) from test where id =10
when 20 then select count(id) from test where id =20
when 30 then select count(id) from test where id =30
when 40 then select count(id) from test where id =40
else 0 END ) col2 from test
throws the error:
missing expression

From looking at the desired output I guess that you want to number each occurrence of an id by groups; if this is the case using the row_number analytical function should do what you want:
select id, row_number() over (partition by id order by id) as col2
from test
order by id;
See this sample SQL Fiddle
Given you sample source data this would be the output:
| ID | COL2 |
|----|------|
| 10 | 1 |
| 10 | 2 |
| 20 | 1 |
| 20 | 2 |
| 20 | 3 |
| 30 | 1 |
| 30 | 2 |
| 40 | 1 |

Related

Get N results for each group without using join

can I solve this without using join? there are so many data in this table, I want to do it more efficiently.
one of my idea is get ID list by using group_concat subquery, but it doesn't work well with IN clause.
SELECT * FROM table WHERE id IN (group_concat subquery)
May I get your advice?
data
ID SERVER_ID ...
--------------------
1 1 ...
2 1
3 1
4 2
5 2
6 2
7 3
8 3
9 3
10 3
...
expected result with limit 2 per each group:
ID SERVER_ID ...
--------------------
1 1 ...
2 1
4 2
5 2
7 3
8 3
You can try the following using row_number, this solution will work for postgreSQL, MySQL 8.0, Oracle and SQL Server.
select
id,
server_id
from
(
select
id,
server_id,
row_number() over (partition by server_id order by id) as rnk
from yourTable
) val
where rnk <= 2
Here is the demo.
| id | server_id |
| --- | --------- |
| 1 | 1 |
| 2 | 1 |
| 4 | 2 |
| 5 | 2 |
| 7 | 3 |
| 8 | 3 |

Adding values from previous row into current row

I have a simple table and I want add a value from previous row into current and value of current in next row so it keeps on going until the end.
Please have a look at the following table data.
ID Value ValueIncrement
1 2 0
2 3 5 (2+3)
3 9 14 (2+3+9)
4 6 20 (2+3+9+6)
5 3 23 (2+3+9+6+3)
6 1 24 (2+3+9+6+3+1)
7 2 26 (2+3+9+6+3+1+2)
8 0 26 (2+3+9+6+3+1+2+0)
9 2 30 (2+3+9+6+3+1+2+0+2)
I am looking for a select query which can enable me to add values continuously.
You can do a window sum:
select id, value, sum(value) over (order by id) valueincrement
from mytable
order by id
Demo on SQL Server 2014 Fiddle:
id | value | valueincrement
-: | ----: | -------------:
1 | 2 | 2
2 | 3 | 5
3 | 9 | 14
4 | 6 | 20
5 | 3 | 23
6 | 1 | 24
7 | 2 | 26
8 | 0 | 26
9 | 2 | 28
You could also use a correlated sub-query if you are working with a small table (otherwise,for performance reason, you should absolutely not use this approach if you can use window-functions). I am assuming your valueincrement column starting with a 0 is a typo and not by design. Let us know if otherwise.
select *, (select sum(value) from mytable t2 where t1.id >= t2.id) as valueincrement
from mytable t1
order by id;
You can try a recursive sum
INSERT INTO #tmpPrueba
VALUES(2),(3),(9),(6),(3),(1),(2),(0),(2)
CREATE TABLE #tmpTest
(nId INT IDENTITY(1,1),
nValor INT NOT NULL)
SELECT *
FROM #tmpTest;
SELECT SUM(nValor) OVER (ORDER BY nId ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
FROM #tmpTest;

Group by similar number SQL (oracle sql)

I would like to find the number of sales that have a similar purchase value from the following table:
sale_number | value
------------+-------
1 | 10
2 | 11
3 | 21
4 | 30
A vanilla group by statement groups by exact value:
select count(sale_number), value from table group by value
Would give:
count(sale_number) | value
------------+-------
1 | 10
1 | 11
1 | 21
1 | 30
Is it possible to group by inexact numbers with a threshold (say +/- 10%)? Giving the desired result:
count(sale_number) | value
------------+-------
2 | 10
2 | 11
1 | 21
1 | 30
You can do what you want with a correlated subquery:
select t.*,
(select count(*)
from t t2
where t2.value >= t.value * 0.9 and
t2.value <= t.value * 1.1
) as cnt
from t;

Count consecutive days

Please I want to count total consecutive days for an event record and order by this record grouping by actor id . for instance we have. For sqlite
event_id| created_at |actor_id
1 | 2018-07-01| 40 /* this is a consecutive days
1 | 2018-07-02| 40 */
1 | 2018-07-04| 40
1 | 2018-07-05| 40
1 | 2018-07-09| 40
2 | 2018-07-11| 40
2 | 2018-07-12| 40
1 | 2018-07-13| 41
should give me something like
actor_id|streak
40 | 3
41 | 0
You can group by actor_id and sum conditionally if there exists a consecutive day:
select
t.actor_id,
sum(case when exists (
select 1 from tablename
where
actor_id = t.actor_id and
julianday(created_at) - julianday(t.created_at) = 1
) then 1 else 0 end) streak
from tablename t
group by t.actor_id
See the demo.
Or with a self join:
select
t.actor_id,
sum(tt.created_at is not null) streak
from tablename t left join tablename tt
on tt.actor_id = t.actor_id and julianday(tt.created_at) - julianday(t.created_at) = 1
group by t.actor_id
See the demo.
Results:
| actor_id | streak |
| -------- | ------ |
| 40 | 3 |
| 41 | 0 |

Hard aggregation query

I'm using Oracle SQL, and i need some help with a query. I have no idea how to do that.
I have the following table (table_a):
Mortgage_ID (int)
Doc_ID (int)
Status (varchar)
Each document can be sent many times for the same mortgage.
From the table above i've made the following table (table_b):
Rank (int)
Document_type (int)
Count (int)
This table is containing the global count of the top 40 popular documents from table_a (regardless the status). For example:
Rank | Doc_ID | count
--------------------------
1 | 212121 | 90
2 | 555111 | 82
3 | 4567654 | 76
. | . | .
. | . | .
. | . | .
40 | 54321 | 22
Now i need to create the following table: For each mortgage from table_a, I need the count of the documents that has been sent for each one of the top 40 documents with the status "OK".
For example:
Mortgage_id | Pop1 | Pop2 | Pop3 | ... | Pop40
-------------------------------------------------
123 | 50 | 21 | 30 | ... | 6
555 | 70 | 0 | 21 | ... | 40
654 | 100 | 96 | 58 | ... | 0
Pop1 doc (the most popular document) has been sent 50 times with the status "OK" for Mortgage_ID 123. Pop2 has been sent 21 times with status "OK" for Mortgage_id 123 and so on.
I hope the description is clear enough. Is anyone knows how to do that?
Basically, this is a join to combine the two tables and then a pivot. In this case, I would use conditional aggregation. So, I think this is what you are looking for:
select a.mortgage_id,
sum(case when b.rank = 1 then 1 else 0 end) as pop1,
sum(case when b.rank = 2 then 1 else 0 end) as pop2,
. . .
sum(case when b.rank = 40 then 1 else 0 end) as pop40
from table_b b join
table_a a
on b.doc_id = a.doc_id
group by a.mortgage_id;
Try this:
select *
from (select ta.Mortgage_ID, rank, cnt
from table_a ta, table_b tb
where ta.doc_id = tb.doc_id
)
pivot (
sum(cnt)
for rank in (1 pop1,2 pop2,3 pop3,4 pop4,5 pop5)
)
MORTGAGE_ID POP1 POP2 POP3 POP4 POP5
----------- ---------- ---------- ---------- ---------- ----------
1 20
2 40
5 10
4 5
3 30
SQLFiddle