I am stuck in one point i have to pick the data from the customer table which has values of customer_id and amount_paid . I want to show a result in the form that first 3 values of the user should be visible in a column name group as a text Group1 and 4 to 10 values of that user to get text Group2 and rest Group3 .
Can you please tell me how to group the values for every customer ?
Thanks
I want to show a result in the form that first 3 values of the user should be visible in a column name group as a text Group1 and 4 to 10 values of that user to get text Group2 and rest Group3
Below is for BigQuery Standard SQL
#standardSQL
SELECT
user_id,
CASE
WHEN pos BETWEEN 1 AND 3 THEN 1
WHEN pos BETWEEN 4 AND 10 THEN 2
ELSE 3
END grp,
SUM(amount_paid) amount_paid
FROM (
SELECT
user_id, amount_paid,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY amount_paid DESC) pos
FROM customer
)
GROUP BY user_id, grp
-- ORDER BY user_id, grp
You can test / play with below dummy generated data
#standardSQL
WITH users AS (
SELECT user_id FROM UNNEST(GENERATE_ARRAY(1,5)) user_id
),
amounts AS (
SELECT ROUND(50 * RAND()) amount_paid FROM UNNEST(GENERATE_ARRAY(1,50)) amount_paid
),
customer AS (
SELECT user_id, ROUND(amount_paid * RAND()) amount_paid
FROM users
CROSS JOIN amounts
)
SELECT
user_id,
CASE
WHEN pos BETWEEN 1 AND 3 THEN 1
WHEN pos BETWEEN 4 AND 10 THEN 2
ELSE 3
END grp,
SUM(amount_paid) amount_paid
FROM (
SELECT
user_id, amount_paid,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY amount_paid DESC) pos
FROM customer
)
GROUP BY user_id, grp
ORDER BY user_id, grp
The output will look like below
user_id grp amount_paid
1 1 147.0
1 2 323.0
1 3 879.0
2 1 147.0
2 2 323.0
2 3 879.0
. . .
so you still need calculate share which (from your question and hopefully) is not a problem for you
added share calculation
#standardSQL
WITH grps AS (
SELECT
user_id,
CASE
WHEN pos BETWEEN 1 AND 3 THEN 1
WHEN pos BETWEEN 4 AND 10 THEN 2
ELSE 3
END grp,
SUM(amount_paid) amount_paid
FROM (
SELECT
user_id, amount_paid,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY amount_paid DESC) pos
FROM customer
)
GROUP BY user_id, grp
)
SELECT * ,
ROUND(amount_paid / SUM(amount_paid) OVER(PARTITION BY user_id), 3) share
FROM grps
-- ORDER BY user_id, grp
Related
how to create columns from unique values of a column and do a cumulative addition or subtraction.
I have a record of transactions like this
transaction_id
created_at
transaction_type
amount
124
2020-08-06 17:00:09
2
25.00
123
2020-08-06 17:00:03
1
50.00
There are various types of transactions, which in turn have different effects .Some results are withdrawals from th account, some are deposits and others don’t affect it at all. This information is summarized in another table (let’s call it ‘transaction_types’), as shown below:
id
description
effect
1
Manual Deposit
add
2
Direct Payment
subtract
id in the table transaction_type is a foreign key transactions. transaction_type = transactions_type.id
now if the initial amount is $10000 I need to create a table like this
transaction_id
initial_balance
deposit
withdrawal
final_balance
123
100000
50
100050
124
100050
25
100025
I don't know how to create new columns based on unique values in some columns also to start a cumulative sum from 10000. This is the query I tried select f.transaction_id, t.created_at, sum(case when d.effect = 'subtract' then -1 else 1 end * amount) from f inner join d on f.transaction_type = d.id
WITH RECURSIVE RowNums AS
(
SELECT
transaction_id,
created_at,
CAST(amount AS SIGNED) AS amount,
effect,
ROW_NUMBER() OVER
(
ORDER BY
created_at,
transaction_id
) AS RowNum
FROM transactions t
JOIN transaction_types tt
ON t.transaction_type = tt.id
),
Balances AS
(
SELECT
transaction_id,
created_at,
amount,
effect,
100000 AS initial_balance,
100000 +
(
CASE effect
WHEN 'add' THEN amount
WHEN 'subtract' THEN (-(amount))
END
) AS final_balance,
RowNum
FROM RowNums
WHERE RowNum = 1
UNION ALL
SELECT
rn.transaction_id,
rn.created_at,
rn.amount,
rn.effect,
b.final_balance,
b.final_balance +
(
CASE rn.effect
WHEN 'add' THEN rn.amount
WHEN 'subtract' THEN (-(rn.amount))
END
),
rn.RowNum
FROM Balances b
JOIN RowNums rn
ON (b.RowNum + 1) = rn.RowNum
)
SELECT
transaction_id,
initial_balance,
CASE effect
WHEN 'add'
THEN CAST(amount AS CHAR(20))
ELSE ''
END AS deposit,
CASE effect
WHEN 'subtract'
THEN CAST(amount AS CHAR(20))
ELSE ''
END AS withdrawal,
final_balance
FROM Balances
ORDER BY
created_at,
transaction_id
source : https://www.reddit.com/r/sqltutorial/comments/sx3ycb/comment/hxr6f9l/?utm_source=share&utm_medium=web2x&context=3
So, my table is :
*uid
code
qty
1
CODE2200
5
2
CODE2205
6
3
CODE0002
2
...
...
...
I want to fetch only first maximum of 10 from qty.
The result i want is :
*uid
code
qty
1
CODE2200
5
2
CODE2205
6
What i know is, limit is for counting the first 'n' row, so it cant be used here.
I'm trying to use WHERE SUM(qty) <= 10 also not work.
You can use window function :
select uid, code, qty
from (select t.*, sum(qty) over(order by uid) as qtysum
from t
) t
where qtysum <= 10;
You can use a cumulative sum. To get the first row that exceeds 10:
select t.*
from (select t.*,
sum(qty) over (order by uid) as running_qty
from t
) t
where running_qty - qty < 10;
If you wanted the first row that hits the threshold of 10:
select t.*
from (select t.*,
sum(qty) over (order by uid) as running_qty
from t
) t
where running_qty - qty < 10 and
qty >= 10;
For example, we have 1, 2 and 3 are the most frequent values at the same time, how to return them when it is a tie?
id
1
1
1
2
2
2
3
3
3
4
You could try:
SELECT id
FROM yourTable
GROUP BY id
HAVING COUNT(*) = (SELECT COUNT(*) FROM yourTable
GROUP BY id ORDER BY COUNT(*) DESC LIMIT 1);
On more recent versions of MySQL 8+, we can use RANK here:
WITH cte AS (
SELECT id, RANK() OVER (ORDER BY COUNT(*) DESC) rnk
FROM yourTable
GROUP BY id
)
SELECT id
FROM cte
WHERE rnk = 1;
I want to get data from database with UNION , I defined link as newslink, and pic as articlepic but it show articlepic data under newslink column, how can I fix this?
SELECT * FROM
((SELECT date, link as newslink FROM news ORDER BY id DESC)
UNION
(SELECT date, pic as articlepic FROM article ORDER BY id DESC)) as x
ORDER BY date DESC LIMIT 6
Sample Data
I want to get articlepic data under articlepic column, and newslink under newslink column
if you values in different column you must add null value in the select for not corresponding columns
SELECT * FROM
((SELECT date, link as newslink, null as articlepic
FROM news ORDER BY id DESC)
UNION
(SELECT date, null, pic
FROM article ORDER BY id DESC)) as x
ORDER BY date DESC LIMIT 6
You are not able to get the different name of the column when using union in the query
SELECT * FROM
(
(
SELECT
DATE,
link AS newslink,
'-' As articlepic
FROM news ORDER BY id DESC
) UNION (
SELECT
DATE,
'-' As newslink,
pic AS articlepic
FROM article ORDER BY id DESC
)
) AS X
ORDER BY DATE DESC LIMIT 6
Say I have the following:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age FROM (
SELECT name, age FROM clubAmembers
UNION
SELECT name, age FROM clubBmembers
)
) AS atable
How can I make it such that I can "stick in a new row at the beginning of the table" prior to the SELECT union such that it would start with:
rownum | name| age
1 | "Jordan" | 6 <-- This is an arbitrarily inserted record with name="Jordan" age="6" that is not a part of any of the clubAmembers or clubBmembers table.
The rest of the table (rownum 2 and onwards) would contain the actual result form the union with clubAmembers then clubBmembers.
Basically I am looking for:
CREATE TABLE
INSERT a row "Jordan" | 6
Perform select with union such that the rows after the first would start with "rownum=2", all the data from clubAmembers, etc.
How to best do this?
"At the beginning of the table" is not truly meaningful to relational databases because the order results are returned are not guaranteed until you use an ORDER BY clause, at which point the order on disk becomes a moot point anyway.
In your case, since you want to guarantee an order in your result clause (and therefore ordering #rownum, you will have to use ORDER BY. Something like:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age
FROM (
SELECT 'Jordan' AS name, 6 AS age, 0 AS ord
UNION
SELECT name, age, 1 AS ord FROM clubAmembers
UNION
SELECT name, age, 1 AS ord FROM clubBmembers
ORDER BY ord
)
) AS atable
Note that at no point does this guarantee that rows in clubAmembers will have a lower rownum than rows in clubBmembers. If you want to guarantee that clubAmembers have a lower rownum, while keeping the semantics of UNION (versus UNION ALL), you can use the following:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age
FROM (
SELECT 'Jordan' AS name, 6 AS age, 0 AS ord
UNION ALL
SELECT name, age, 1 AS ord FROM clubAmembers
UNION ALL
SELECT name, age, 2 AS ord FROM clubBmembers AS b
WHERE NOT EXISTS(SELECT 1 FROM clubAmembers AS a
WHERE a.name = b.name AND a.age = b.age)
ORDER BY ord
)
) AS atable
Note if {name, age} could be duplicated within the clubXmembers table, you will need to add DISTINCT:
...
SELECT DISTINCT name, age, 1 AS ord FROM clubAmembers
UNION ALL
...
As per the request in the comments, if you had a clubCmembers table, you would do:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age
FROM (
SELECT 'Jordan' AS name, 6 AS age, 0 AS ord
UNION ALL
SELECT name, age, 1 AS ord FROM clubAmembers
UNION ALL
SELECT name, age, 2 AS ord FROM clubBmembers AS b
WHERE NOT EXISTS(SELECT 1 FROM clubAmembers AS a
WHERE a.name = b.name AND a.age = b.age)
SELECT name, age, 3 AS ord FROM clubCmembers AS c
WHERE NOT EXISTS(SELECT 1 FROM clubAmembers AS a
WHERE a.name = c.name AND a.age = c.age)
AND NOT EXISTS(SELECT 1 FROM clubBmembers AS b
WHERE b.name = c.name AND b.age = c.age)
ORDER BY ord
)
) AS atable
I'm not sure if I got it right. But why don't you just add another union like this:
CREATE TABLE newtable AS (
SELECT #rownum:=#rownum+1 as rownum, name, age FROM (
SELECT 1, "Jordan", 6
UNION ALL
SELECT name, age FROM clubAmembers
UNION ALL
SELECT name, age FROM clubBmembers
)
) AS atable
You can separate the create table statmenet from the insert statmenet:
Create the table (you must know which colums are gona be there)
Insert your 1st record (INSERT INTO .... Values(...))
Use your statement but with insert into instead of create table like: INSERT INTO YourNewTable.... Values(YourSubQuery) (Nr and tye of columns must match your subquery)
This should do, I believe:
CREATE TABLE newtable AS (
SELECT (#rownum:=IFNULL(#rownum,0)+1)+1 as rownum, name, age FROM (
SELECT name, age FROM clubAmembers
UNION
SELECT name, age FROM clubBmembers
) AS s
UNION ALL
SELECT 1, 'Jordan', 6
) AS atable
Demo at SQL Fiddle: http://sqlfiddle.com/#!2/ab825/6