count real total customer by product groupby - sql

first of all, i'm not really sure if this possible or not.
let say I have this dataset example
CREATE TABLE TRANSACTION(
user_id numeric,
account_id varchar,
product varchar,
colour varchar,
price numeric);
insert into transaction (user_id, account_id, product, colour, price)
values
(1, 'a1', 'biycle', 'black', 500),
(1, 'a2', 'motorbike', 'red', 1000),
(1, 'a2', 'motorbike', 'blue', 1200),
(2, 'b3', 'car', 'grey', 10000),
(2, 'b2', 'motorbike', 'black', 1250),
(3, 'c1', 'biycle', 'black', 500),
(3, 'c2', 'biycle', 'black', 525),
(3, 'c4', 'skateboard', 'white', 250),
(3, 'c5', 'scooter', 'blue', 260)
from that table we know that
the total real customer is 3 (1,2,3) and
the total real account is 8 (a1, a2, b3, b2, c1, c2, c4, c5)
and then with this code
SELECT
product,
colour,
sum(price)total_price,
count(DISTINCT user_id)customer_total,
count(DISTINCT account_id)account_total
from transaction
group by
product, colour
and the return is like this
product
colour
total_price
customer_total
account_total
biycle
black
1525
2
3
car
grey
10000
1
1
motorbike
black
1250
1
1
motorbike
blue
1200
1
1
motorbike
red
1000
1
1
scooter
blue
260
1
1
skateboard
white
250
1
1
from the output above,
if we total the customer_total, it will be 8 and
if we total the account_total, it will be 9
is there any alternative way so that the customer_total will be 3 and account_total will be 8

You can calculate the accounts and customer total using an inline query that computes the customer and account totals within the same query.
SELECT
product,
colour,
sum(price)total_price,
(select count(DISTINCT user_id) from transaction) as customer_total,
(select count(DISTINCT account_id) from transaction) as account_total
from transaction
group by
product, colour
Result:
product
colour
total_price
customer_total
account_total
biycle
black
1525
3
8
car
grey
10000
3
8
motorbike
black
1250
3
8
motorbike
blue
1200
3
8
motorbike
red
1000
3
8
scooter
blue
260
3
8
skateboard
white
250
3
8

Related

Using aggregate on another aggregate function - MAX() on an aggregate

I've a tournament bracket consisting of 2 Groups (Group A and Group B).
I already have a query where I retreive some information such as the average rating, amount of teams etc.
To the problem: I don't want to allow 2 teams of the same group having the same color. I want a column representing the MAX() of ColorInterference without having to make a subselect.
Is this possible or am I forced to make a SELECT MAX(ColorInterference) over the query result?
Group
Team
Color
Rating
TeamsCount
AverageRating
AverageRatingGroup
ColorInterference
A
Helos
Green
1452
8
1518
1544
0
A
Pelicans
Purple
1687
8
1518
1544
0
A
Epic Square Dance
Red
1498
8
1518
1544
0
A
Narnia Ninjas
Yellow
1542
8
1518
1544
0
B
O.T.
Blue
1502
8
1518
1492
0
B
Helos
Green
1452
8
1518
1492
1
B
Treasure Goggles
Green
1485
8
1518
1492
1
B
Red Off
Yellow
1530
8
1518
1492
0
DECLARE #Bracket_Groups Table ([Group] nvarchar(10), Team nvarchar(50), Color nvarchar(50), Rating int)
INSERT INTO #Bracket_Groups(Team, [Group], Color, Rating)
SELECT 'Narnia Ninjas', 'A', 'Yellow' , 1542
UNION SELECT 'Helos', 'A', 'Green', 1452
UNION SELECT 'Pelicans', 'A', 'Purple', 1687
UNION SELECT 'Epic Square Dance', 'A', 'Red', 1498
UNION SELECT 'O.T.', 'B', 'Blue', 1502
UNION SELECT 'Red Off', 'B', 'Yellow', 1530
UNION SELECT 'Helos', 'B', 'Green', 1452
UNION SELECT 'Treasure Goggles', 'B', 'Green', 1485
SELECT
[Group]
, Team
, Color
, Rating
, COUNT(*) OVER() as TeamsCount
, AVG(Rating) OVER () as AverageRating
, AVG(Rating) OVER (Partition By [Group]) as Group_AverageRating
, SIGN(COUNT(Color) OVER (partition By [Group], Color) - 1) as ColorInterference
FROM #Bracket_Groups
Order by [Group]

sql: comparting two tables with different values produces duplicated results

I want to join/union two tables that have primary key, category, and a score, in such a way, that results will show the primary key and all categories and scores present in both tables together, and, if a given category is in only one table, then with a null for the score from the second table.
The tables are as follow:
opinion_1
fruit category score
apple color 15
apple sweet 50
apple scent 35
orange color 40
orange sweet 60
opinion_2
fruit category score
apple color 28
apple sweet 12
orange color 29
orange sweet 50
orange scent 31
I've tried full outer joining and double left joining with union, getting the same results with categories incorrectly multiplied.
WITH opinion_1 AS (
SELECT 'apple' as fruit, 'color' as category, 15 as score UNION ALL
SELECT 'apple', 'sweet', 50 UNION ALL
SELECT 'apple', 'scent', 35 UNION ALL
SELECT 'orange', 'color', 40 UNION ALL
SELECT 'orange', 'sweet', 60
), opinion_2 AS (
SELECT 'apple' as fruit, 'color' as category, 28 as score UNION ALL
SELECT 'apple', 'sweet', 12 UNION ALL
SELECT 'orange', 'color', 29 UNION ALL
SELECT 'orange', 'sweet', 50 UNION ALL
SELECT 'orange', 'scent', 31
)
SELECT
opinion_1.fruit,
opinion_1.category as category,
opinion_1.score as score1,
opinion_2.score as score2
FROM opinion_1
full outer join opinion_2
on opinion_1.fruit = opinion_2.fruit
I expect the following result of the operation:
fruit category score1 score2
apple color 15 28
apple sweet 50 12
apple scent 35 null
orange color 40 29
orange sweet 60 50
orange scent null 31
but I'm getting this:
fruit category score1 score2
apple color 15 12
apple color 15 28
apple sweet 50 12
apple sweet 50 28
apple scent 35 12
apple scent 35 28
orange color 40 50
orange color 40 31
orange color 40 29
orange sweet 60 50
orange sweet 60 31
orange sweet 60 29
I think you are missing a condition on your join to get the result you expect. Moreover, selecting opinion_1.fruit and opinion_1.category will produce nulls if there is no records for some fruit on opinion_1 whilst there are on opinion_2. The following query will produce the expected result :
WITH opinion_1 AS (
SELECT 'apple' as fruit, 'color' as category, 15 as score UNION ALL
SELECT 'apple', 'sweet', 50 UNION ALL
SELECT 'apple', 'scent', 35 UNION ALL
SELECT 'orange', 'color', 40 UNION ALL
SELECT 'orange', 'sweet', 60
), opinion_2 AS (
SELECT 'apple' as fruit, 'color' as category, 28 as score UNION ALL
SELECT 'apple', 'sweet', 12 UNION ALL
SELECT 'orange', 'color', 29 UNION ALL
SELECT 'orange', 'sweet', 50 UNION ALL
SELECT 'orange', 'scent', 31
)
SELECT
coalesce(opinion_1.fruit, opinion_2.fruit) as fruit,
coalesce(opinion_1.category, opinion_2.category) as category,
opinion_1.score as score1,
opinion_2.score as score2
FROM opinion_1
full outer join opinion_2
on opinion_1.fruit = opinion_2.fruit and opinion_1.category = opinion_2.category
Below is for BigQuery Standard SQL
#standardSQL
WITH opinion_1 AS (
SELECT 'apple' AS fruit, 'color' AS category, 15 AS score UNION ALL
SELECT 'apple', 'sweet', 50 UNION ALL
SELECT 'apple', 'scent', 35 UNION ALL
SELECT 'orange', 'color', 40 UNION ALL
SELECT 'orange', 'sweet', 60
), opinion_2 AS (
SELECT 'apple' AS fruit, 'color' AS category, 28 AS score UNION ALL
SELECT 'apple', 'sweet', 12 UNION ALL
SELECT 'orange', 'color', 29 UNION ALL
SELECT 'orange', 'sweet', 50 UNION ALL
SELECT 'orange', 'scent', 31
)
SELECT
IFNULL(a.fruit, b.fruit) fruit,
IFNULL(a.category, b.category) AS category,
a.score AS score1,
b.score AS score2
FROM opinion_1 a
FULL OUTER JOIN opinion_2 b
USING(fruit, category)
with result
Row fruit category score1 score2
1 apple color 15 28
2 apple sweet 50 12
3 apple scent 35 null
4 orange color 40 29
5 orange sweet 60 50
6 orange scent null 31

MS SQL Server where exist same rows

I have table like this
TABLE1
DATE NAME PRODUCT VERSION Colour SIZE ID
2017-08-03 01:30:20.000 Bob Mouse 12 Pink 3 461
2017-08-03 01:30:20.000 Bob Mouse 12 Pink 3 446
2017-08-03 01:30:20.000 Bob Mouse 13 Pink 3 487
2017-08-03 01:30:20.000 Bob Honey 6 Red 5 476
2017-08-03 01:30:20.000 Bob Honey 6 Blue 5 774
2017-08-03 01:30:20.000 Bob Shoe 6 Black 5 487
2017-08-03 01:30:20.000 Bob Dog 5 Black 7 1874
2017-08-03 01:30:20.000 Bob Dog 5 Black 7 999
2017-08-03 01:30:20.000 Bob Pet 689 Red 9 855
2017-08-02 01:30:20.000 Eva Mouse 12 Pink 3 461
2017-08-02 01:30:20.000 Eva Mouse 12 Pink 3 446
And the result should look like this:
TABLE1
DATE NAME PRODUCT VERSION Colour SIZE ID
2017-08-03 01:30:20.000 Bob Mouse 12 Ping 3 446
2017-08-03 01:30:20.000 Bob Mouse 13 Ping 3 487
2017-08-03 01:30:20.000 Bob Honey 6 Red 5 476
2017-08-03 01:30:20.000 Bob Shoe 6 Black 5 487
2017-08-03 01:30:20.000 Bob Dog 5 Black 7 1874
2017-08-03 01:30:20.000 Bob Pet 689 Red 9 855
2017-08-02 01:30:20.000 Eva Mouse 12 Pink 3 446
Main thing i am interest for is : Product AND Version
If is different something else should by deleted rows which contain Same PRODUCT AND VERSION
Problem is with column ID where I have always different ID numbers, but with Different NAME I have Same ID
There are examples if you need just to select such data or if you need to delete it:
create table #test
(
[DATE] DateTime2(7),
[NAME] VARCHAR(200),
[PRODUCT] VARCHAR(200),
[VERSION] INT,
[Colour] VARCHAR(200),
[SIZE] INT,
[ID] INT
);
INSERT INTO #test VALUES ('2017-08-03 01:30:20.000', 'Bob', 'Mouse', 12, 'Pink', 3, 461);
INSERT INTO #test VALUES ('2017-08-03 01:30:20.000', 'Bob', 'Mouse', 12, 'Pink', 3, 446);
INSERT INTO #test VALUES ('2017-08-03 01:30:20.000', 'Bob', 'Mouse', 13, 'Pink', 3, 487);
INSERT INTO #test VALUES ('2017-08-03 01:30:20.000', 'Bob', 'Honey', 6, 'Red', 5, 476);
INSERT INTO #test VALUES ('2017-08-03 01:30:20.000', 'Bob', 'Honey', 6, 'Blue', 5, 774);
INSERT INTO #test VALUES ('2017-08-03 01:30:20.000', 'Bob', 'Shoe', 6, 'Black', 5, 487);
INSERT INTO #test VALUES ('2017-08-03 01:30:20.000', 'Bob', 'Dog', 5, 'Black', 7, 1874);
INSERT INTO #test VALUES ('2017-08-03 01:30:20.000', 'Bob', 'Dog', 5, 'Black', 7, 999);
INSERT INTO #test VALUES ('2017-08-03 01:30:20.000', 'Bob', 'Pet', 689, 'Red', 9, 855);
INSERT INTO #test VALUES ('2017-08-02 01:30:20.000', 'Eva', 'Mouse', 12, 'Pink', 3, 461);
INSERT INTO #test VALUES ('2017-08-02 01:30:20.000', 'Eva', 'Mouse', 12, 'Pink', 3, 446);
SELECT
[DATE] ,
[NAME],
[PRODUCT],
[VERSION],
[Colour],
[SIZE],
[ID]
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [PRODUCT], [VERSION] ORDER BY [ID]) rn
FROM #test
) a
WHERE rn = 1;
DELETE FROM #test WHERE ID IN (SELECT
[ID]
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [PRODUCT], [VERSION] ORDER BY [ID]) rn
FROM #test
) a
WHERE rn > 1);
SELECT * FROM #test;
DROP TABLE #test;

Oracle SQL Query Get the remaining amount by group

I need to find the remaining amount for each credit no. The expected result is this:
CREDIT_NO CREDIT_TYPE CREDIT_AMOUNT TOTAL_A REMAINING_AMT FINAL_TOTAL_PER_BILL
A1 W 100 1000 900 600
A1 X 100 1000 800 600
A1 Y 100 1000 700 600
A1 Z 100 1000 600 600
B1 X 100 2000 1900 1700
B1 Y 100 2000 1800 1700
B1 Z 100 2000 1700 1700
Here's the query I have done so far (please pardon the noob):
WITH TEMP AS
(SELECT 1 ID,
'A1' CREDIT_NO,
'X' CREDIT_TYPE,
100 CREDIT_AMOUNT,
1000 TOTAL_A
FROM DUAL
UNION ALL
SELECT 2, 'A1' , 'Y', 100, 1000 FROM DUAL
UNION ALL
SELECT 4, 'A1' , 'Z', 100, 1000 FROM DUAL
UNION ALL
SELECT 3, 'B1', 'X', 100, 2000 FROM DUAL
UNION ALL
SELECT 5, 'B1', 'Y', 100, 2000 FROM DUAL
UNION ALL
SELECT 6, 'B1', 'Z', 100, 2000 FROM DUAL
UNION ALL
SELECT 7, 'A1', 'W', 100, 1000 FROM DUAL
)
SELECT
TEMP1.CREDIT_NO ,
TEMP1.CREDIT_TYPE,
TEMP1.CREDIT_AMOUNT ,
TEMP1.TOTAL_A ,
CASE
WHEN TEMP1.CREDIT_NO = (LAG (TEMP1.CREDIT_NO,1) OVER (ORDER BY TEMP1.CREDIT_NO) ) -- set remaining CREDIT_AMOUNT
OR (LAG (TEMP1.CREDIT_NO,1) OVER (ORDER BY TEMP1.CREDIT_NO) ) IS NULL
THEN TEMP1.TOTAL_A - (SUM(TEMP1.CREDIT_AMOUNT) OVER ( ORDER BY TEMP1.CREDIT_NO ROWS BETWEEN
UNBOUNDED PRECEDING
AND CURRENT ROW ) )
WHEN TEMP1.CREDIT_NO <> -- new bill, new total CREDIT_AMOUNT
(LAG (TEMP1.CREDIT_NO,1) OVER (ORDER BY TEMP1.CREDIT_NO) )
THEN TEMP1.TOTAL_A - TEMP1.CREDIT_AMOUNT
END AS REMAINING_AMT
,TEMP1.TOTAL_A - (SUM(TEMP1.CREDIT_AMOUNT) OVER (PARTITION BY CREDIT_NO)) AS FINAL_TOTAL_PER_BILL
FROM TEMP TEMP1
ORDER BY CREDIT_NO, CREDIT_TYPE
My problem is I don't know how to compute for the remaining amount for the 2nd credit no. The result of the above query is:
CREDIT_NO CREDIT_TYPE CREDIT_AMOUNT TOTAL_A REMAINING_AMT FINAL_TOTAL_PER_BILL
A1 W 100 1000 900 600
A1 X 100 1000 800 600
A1 Y 100 1000 700 600
A1 Z 100 1000 600 600
B1 X 100 2000 1900 1700
B1 Y 100 2000 1400 1700
B1 Z 100 2000 1300 1700
Is it possible to get a running remaining amount without using a stored procedure? I tried basing it on the rownum but it is not sequential.
Even though I have found similar questions to this (Link 1, Link 2, Link 3)
(I'm still going over the third link though), I hope you guys can help me.
use a running subtotal, and be careful at partition clause as:
select credit_no, credit_type,
total_a - sum(credit_amount) over (partition by credit_no order by id) as remaining_credit,
total_a,
total_a - sum(credit_amount) over (partition by credit_no) as FINAL_TOTAL_PER_BILL
from temp
see sqlfiddle

How to filter rows on a complex filter

I have these rows in a table
ID Name Price Delivery
== ==== ===== ========
1 apple 1 1
2 apple 3 2
3 apple 6 3
4 apple 9 4
5 orange 4 6
6 orange 5 7
I want to have the price at the third delivery (Delivery=3) or the last price if there's no third delivery.
It would give me this :
ID Name Price Delivery
== ==== ===== ========
3 apple 6 3
6 orange 5 7
I don't necessary want a full solution but an idea of what to look for would be greatly appreciated.
SQL> create table t (id,name,price,delivery)
2 as
3 select 1, 'apple', 1, 1 from dual union all
4 select 2, 'apple', 3, 2 from dual union all
5 select 3, 'apple', 6, 3 from dual union all
6 select 4, 'apple', 9, 4 from dual union all
7 select 5, 'orange', 4, 6 from dual union all
8 select 6, 'orange', 5, 7 from dual
9 /
Table created.
SQL> select max(id) keep (dense_rank last order by nullif(delivery,3) nulls last) id
2 , name
3 , max(price) keep (dense_rank last order by nullif(delivery,3) nulls last) price
4 , max(delivery) keep (dense_rank last order by nullif(delivery,3) nulls last) delivery
5 from t
6 group by name
7 /
ID NAME PRICE DELIVERY
---------- ------ ---------- ----------
3 apple 6 3
6 orange 5 7
2 rows selected.
EDIT: Since you want "an idea of what to look for", here is an description of why I think this solution is the best, besides being the query with the least amount of lines. Your expected result set indicates that you want to group your data per fruit name ("group by name"). And of each group you want to keep the values of the records with delivery = 3 or when that number doesn't exists, the last one ("keep (dense_rank last order by nullif(delivery,3) nulls last"). In my opinion, the query above just reads like that. And it uses only one table access to get the result, although my query is not unique in that.
Regards,
Rob.
Use ROW_NUMBER twice - once to filter the rows away that are after the third delivery, and the second time to find the last row remaining (i.e. a typical max per group query).
I've implemented this using CTEs. I tested it in SQL Server but I believe that Oracle supports the same syntax.
WITH T1 AS (
SELECT
ID, Name, Price, Delivery,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Delivery) AS rn
FROM Table1
), T2 AS (
SELECT
t1.*,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Delivery DESC) AS rn2
FROM T1
WHERE rn <= 3
)
SELECT ID, Name, Price, Delivery
FROM T2
WHERE rn2 = 1
Result:
ID Name Price Delivery
3 apple 6 3
6 orange 5 7
select t3.ID, t3.Name, t3.Price, t3.Delivery
from (
select Name, max(Delivery) as MaxDelivery
from MyTable
group by Name
) t1
left outer join MyTable t2 on t1.Name = t2.Name and Delivery = 3
inner join MyTable t3 on t1.Name = t3.name
and t3.Delivery = coalesce(t2.Delivery, t1.MaxDelivery)
Mark's and APC's answers work if you meant the third delivery, regardless of the Delivery number. Here's a solution using analytic functions that specifically searches for a record with Delivery = 3.
CREATE TABLE FRUITS (
ID NUMBER,
Name VARCHAR2(10),
Price INTEGER,
Delivery INTEGER);
INSERT INTO FRUITS VALUES (1, 'apple', 1, 1);
INSERT INTO FRUITS VALUES (2, 'apple', 3, 2);
INSERT INTO FRUITS VALUES (3, 'apple', 6, 3);
INSERT INTO FRUITS VALUES (4, 'apple', 9, 4);
INSERT INTO FRUITS VALUES (5, 'orange', 4, 6);
INSERT INTO FRUITS VALUES (6, 'orange', 5, 7);
INSERT INTO FRUITS VALUES (7, 'pear', 2, 5);
INSERT INTO FRUITS VALUES (8, 'pear', 4, 6);
INSERT INTO FRUITS VALUES (9, 'pear', 6, 7);
INSERT INTO FRUITS VALUES (10, 'pear', 8, 8);
SELECT ID,
Name,
Price,
Delivery
FROM (SELECT ID,
Name,
Price,
Delivery,
SUM(CASE WHEN Delivery = 3 THEN 1 ELSE 0 END)
OVER (PARTITION BY Name) AS ThreeCount,
ROW_NUMBER()
OVER (PARTITION BY Name ORDER BY Delivery DESC) AS rn
FROM FRUITS)
WHERE (ThreeCount <> 0 AND Delivery = 3) OR
(ThreeCount = 0 AND rn = 1)
ORDER BY ID;
DROP TABLE FRUITS;
And the results from Oracle XE 10g:
ID Name Price Delivery
---- ---------- ------- ----------
3 apple 6 3
6 orange 5 7
10 pear 8 8
I included a third fruit in the sample data to illustrate the effect of different interpretations of the question. The other solutions would pick ID=9 for the pear.