Pivot the row with column's names into a column using SQL - sql

I have one table with 25 columns and 1 single row and I want to pivot it
and obtain a column with the names of the columns and a column with the corresponding values using SQL. But I do not know how to do it.
I have the following table:
+-------+-------+-------+-----+--------+
| cnt_0 | cnt_1 | cnt_2 | ... | cnt_25 |
+-------|-------|-------+-----|--------+
| 34. | 26 | 15 | ... | 5 |
+-------+-------+-------+-----+--------+
And I want to pivot the table and transform the row of the column names into. a column and obtain this:
+--------+--------+
| counts | amount |
+--------+--------+
| cnt_0 | 34. |
| cnt_1 | 26 |
| cnt_2 | 15 |
| ... | ... |
| cnt_25 | 5 |
+--------+--------+

Verbose, but you could use UNION ALL:
SELECT counts, amount
FROM
(
SELECT 'cnt_0' AS counts, cnt_0 AS amount, 0 AS pos UNION ALL
SELECT 'cnt_1', cnt_1, 1 UNION ALL
SELECT 'cnt_2', cnt_2, 2 UNION ALL
...
SELECT 'cnt_25', cnt_25, 25
) t
ORDER BY pos;

Related

how to dynamically unpivot only those columns with a specific suffix in bigquery

I have a table with a large number of columns (around 900 columns, which makes it unfeasible to individually write all the column names). How can I dynamically unpivot only the columns with the suffix '_next' & '_last' (there are hundreds of such columns)? For example:
TABLE:
+---------+------------+-----------+-------+----------+-----------+
|name |product_next|upload_last|product|books |active_next|
+---------+------------+-----------+-------+----------+-----------+
| alice| a | 100 |apple | 10 | 1 |
| bob| b | 23 |orange | 2 | 0 |
+---------+------------+-----------+-------+----------+-----------+
FINAL TABLE (after unpivoting):
+---------+------------+-----------+-------+----------+
|name |metric |value |product|books |
+---------+------------+-----------+-------+----------+
| alice|product | a |apple | 10 |
| bob|product | b |orange | 2 |
| alice|upload | 100 |apple | 10 |
| bob|upload | 23 |orange | 2 |
| alice|active | 1 |apple | 10 |
| bob|active | 0 |orange | 2 |
+---------+------------+-----------+-------+----------+
Consider below approach
select * from your_table
unpivot (metric for col in (product_next, upload_last, active_next))
if applied to sample data in your question
with your_table as (
select 'alice' name, 'a' product_next, '100' upload_last, 'apple' product, '10' books, '1' active_next union all
select 'bob', 'b', '23', 'orange', '2', '0'
)
output is
Additionally, to #Mikhail Answer that is correct you need to add a WHERE clause with a REGEXP_CONTAINS expression as the following one:
where REGEXP_CONTAINS(col, '_next') OR REGEXP_CONTAINS(col,'_last')
The full Query will be:
select * from your_table
unpivot (metric for col in (product_next, upload_last, active_next))
where REGEXP_CONTAINS(col, '_next') OR REGEXP_CONTAINS(col,'_last')

How to select from a table with additional where clause on a single column

I'm having trouble formulating a SQL query in Oracle. Here's my sample table:
+----+-----------+-----------+--------+
| id | start | end | number |
+----+-----------+-----------+--------+
| 1 | 21-dec-19 | 03-jan-20 | 12 |
| 2 | 23-dec-19 | 05-jan-20 | 10 |
| 3 | 02-jan-20 | 15-jan-20 | 9 |
| 4 | 09-jan-20 | NULL | 11 |
+----+-----------+-----------+--------+
And here's what I have so far:
SELECT
SUM(number) AS total_number,
SUM(number) AS total_ended_number -- (WHERE end IS NOT NULL)
FROM table
WHERE ... -- a lot of where clauses
And the desired result:
+--------------+--------------------+
| total_number | total_ended_number |
+--------------+--------------------+
| 42 | 31 |
+--------------+--------------------+
I understand I could do a separate select inside 'total_ended_number', but the initial select has a bunch of where clauses already which would need to be applied to the internal select as well.
I'm capable of formulating it in 2 separate selects or 2 nested selects with all the where clauses duplicated, but my intended goal is to not duplicate the where clauses that would both be used on the table.
You could sum over a case expression with this logic:
SELECT
SUM(number) AS total_number,
SUM(CASE WHEN end IS NOT NULL THEN number END) AS total_ended_number
FROM table
WHERE ... -- a lot of where clauses
SUM(case when "end" is not null then number else 0 end) AS total_ended_number

Get range (min - max) of values concatenated in a single row

given the following table
+-----------------------------+
| id | type | price | item_id |
|-----------------------------|
| 1 | 1 | 20 | 22 |
|-----------------------------|
| 2 | 1 | 22 | 22 |
|-----------------------------|
| 3 | 2 | 19 | 22 |
|-----------------------------|
| 4 | 2 | 11 | 22 |
|-----------------------------|
| 5 | 1 | 08 | 22 |
|-----------------------------|
| 6 | 2 | 25 | 22 |
+-----------------------------+
I am trying to select the data to create a view as follows in a single row
+-------------------------------------+
| type1_range | type2_range | item_id |
|-------------------------------------|
| 08 - 22 | 11 - 25 | 22 |
+-------------------------------------+
type1_range and type2_range are the minimum and maximum price for each types.
I can get the data in couple of rows using
SELECT type, MAX (price) , MIN (price)
FROM table
where item_id=22 GROUP BY type;
+----------------------------+
| type | max | min | item_id |
|----------------------------|
| 1 | 22 | 08 | 22 |
|----------------------------|
| 2 | 25 | 11 | 22 |
+----------------------------+
But I am trying to concat the rows like this:
+-------------------------------------+
| type1_range | type2_range | item_id |
|-------------------------------------|
| 08 - 22 | 11 - 25 | 22 |
+-------------------------------------+
What would be sql required for this?
Something like this:
SELECT
CONCAT(
MIN(CASE WHEN type = 1 THEN price END),
' - ',
MAX(CASE WHEN type = 1 THEN price END)
) as type1range,
CONCAT(
MIN(CASE WHEN type = 2 THEN price END),
' - ',
MAX(CASE WHEN type = 2 THEN price END)
) as type2range.
item_id
FROM table
WHERE item_id = 22
GROUP BY item_id
You've tagged two different database systems (please avoid doing this) but I believe they do both support CONCAT() for string concatenation
If you want to omit the item_id from the select list (you already know it's item 22) you can remove the GROUP BY. Alternatively if you remove the WHERE and leave the group by you'll get a row for each item_id
To get more of an idea as to how it works, remove the concat and the min/max - you'll see that the case when causes the price to show up only if the type is 1 (in the type 1 range column) otherwise it's null. It's the. Trivial for the min and max to work on just type 1 or just type 2 data for each column. It's actually a form of pivot query if you want to read up on them more
A straight forward approach would be having type1_range and type2_range as two sub-queries and join with the distinct id's like shown below,
SELECT t.item_id,type1_range,type2_range
FROM (Select distinct item_id from table) t
LEFT join
(SELECT item_id,type, concat(MIN(price),'-' ,MAX(price) ) as type1_range
FROM table
where type=1
GROUP BY item_id,type)type1 on type1.item_id=t.item_id
LEFT join
(SELECT item_id,type, concat(MIN(price),'-' ,MAX(price) ) as type2_range
FROM table
where type=2
GROUP BY item_id,type)type2 on type2.item_id=t.item_id

How to select values, where each one depends on a previously aggregated state?

I have the following table:
|-----|-----|
| i d | val |
|-----|-----|
| 1 | 1 |
|-----|-----|
| 2 | 4 |
|-----|-----|
| 3 | 3 |
|-----|-----|
| 4 | 7 |
|-----|-----|
Can I get the following output:
|-----|
| sum |
|-----|
| 1 |
|-----|
| 5 |
|-----|
| 8 |
|-----|
| 1 5 |
|-----|
using a single SQLite3 SELECT-query? I know it could be easily achieved using variables, but SQLite3 lacks those. Maybe some recursive query? Thanks.
No.
In a relational database table rows do not have any order. If you specify an order for the rows, then it's possible to write a query.
Now, you could add an extra column to sort the rows. For example:
| val | sort
|-----|-----
| 1 | 10
| 4 | 20
| 3 | 30
| 7 | 40
The query could be:
select
sum(val) over(order by sort)
from my_table
For the updated question, you can write:
select
sum(val) over(order by id)
from my_table
By using the order of the id column and if you want only the sum column, you can do this:
select (select sum(val) from tablename where id <= t.id) sum
from tablename t

calculating sum of rows with identical id

Let's imagine a table with two columns ex:
| Value | ID |
+-------+----+
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 1 | 2 |
| 2 | 2 |
| 2 | 2 |
What I am trying to do is to calculate the sum of those with similar id and display them in different table like:
| Sum | ID |
+-----+----+
| 9 | 1 |
| 5 | 2 |
and so on.
I could find a sum of a known id by
SELECT SUM(VALUE) FROM MYTABLE WHERE ID = 1;
However not sure on how to find sum of different id's separately, could you give an idea on how to proceed?
Select SUM(VALUE),ID FROM MYTABLE GROUP BY ID
Use GROUP BY clause:
SELECT SUM(VALUE) Sum, ID FROM MYTABLE GROUP BY ID;
SELECT SUM(VALUE),ID FROM MYTABLE Group By ID