I have a table like:
| client_id | product_type |
|-----------|--------------|
| c_1 | toy |
| c_2 | toy |
| c_3 | furniture |
| c_3 | furniture |
| c_3 | book |
| c_4 | toy |
| c_4 | furniture |
| c_5 | book |
| c_5 | book |
| c_5 | book |
As you can see, each client can have multiple products assigned.
I have to categorise each client by products they have.
If clients have product toy assigned to them, they should have toys
in the result table, no matter if they have other products.
If clients have product furniture assigned to them, they should have furnitures in the result table, no matter if they have other products.
If clients have product book assigned to them, they should have books in the result table, no matter if they have other products and so on...
Each client must appear in the results only once.
For the above example the result will be:
| client_id | cat |
|-----------|------------|
| c_1 | toys |
| c_2 | toys |
| c_3 | furnitures |
| c_4 | toys |
| c_5 | books |
You can use a CASE to assign the priority, e.g.
select client_id,
'cat_' +
min(case product_type
when 'toy' then '1'
when 'furniture' then '2'
when 'book' then '3'
end)
from tab
group by client_id
Would be easier if you have a table defining the category/priority for each product, then it's a simple join instead of CASE.
I think the logic to get the results you want is:
select client_id,
(case when sum(case when product_type = 'toy' then 1 else 0 end) > 0
then 'toys'
when sum(case when product_type = 'furniture' then 1 else 0 end) > 0
then 'furnitures'
when sum(case when product_type = 'book' then 1 else 0 end) > 0
then 'books'
end) as category
from t
group by client_id;
Related
Hi my query below is summing multiple values based on #cropseasons in my table. Since i have 4 crop seasons it seems to be multiplying the values by 4 since i have crop season as 1, 2, 3 or 4. All i want is values for 1 crop season. Can anyone assist? I have crop season in both tables.
With Summary as (
Select B_NAME as Branch, LOC as Location
,SUM(payment) as Gallons
,SUM(case when printed = 1 THEN Fee ELSE NULL END) as FeeCollected
,SUM(case when printed = 0 THEN Fee ELSE NULL END) as FeeNotCollected
,SUM(case when printed = 1 THEN Payment ELSE NULL END) as GallonsIssued
,SUM(case when printed = 0 THEN Payment ELSE NULL END) as GallonsNotIssued
From SicbWeeklyDeliveriesFuelArchive F Inner Join FarmerGroups G ON G.BSI_CODE = F.BSI_CODE
Where F.CROP_SEASON = #cropseason
Group By B_NAME, LOC
)
SELECT Branch
,Location
,Gallons
,GallonsIssued
,GallonsNotIssued
,FeeCollected
,FeeNotCollected
,((GallonsIssued/Gallons) * 100) as pct_GallonsCollected
FROM Summary
Order by Location, Branch
SicbWeeklyDeliveriesFuelArchive
+-------+----------+-------------+-----+---------+------+-------------+---------+
| ID | BSI_CODE | B_NAME | LOC | PAYMENT | FEE | CROP_SEASON | PRINTED |
+-------+----------+-------------+-----+---------+------+-------------+---------+
| 18735 | 2176 | SAN NARCISO | CZ | 85 | 8.5 | 4 | 0 |
| 18738 | 2176 | SAN NARCISO | CZ | 65 | 6.5 | 4 | 0 |
| 18739 | 10494 | SAN NARCISO | CZ | 85 | 8.5 | 3 | 0 |
+-------+----------+-------------+-----+---------+------+-------------+---------+
FarmerGroups
+-------+----------+-------------+-------------+
| ID | BSI_CODE | CROP_SEASON | BRANCH |
+-------+----------+-------------+-------------+
| 10473 | 2176 | 4 | SAN NARCISO |
| 11478 | 2176 | 3 | SAN NARCISO |
| 12787 | 10494 | 4 | SAN ROMAN |
+-------+----------+-------------+-------------+
It seems your join criteria is incomplete. The tables share BSI_CODE and CROP_SEASON, so I guess you want:
FROM sicbweeklydeliveriesfuelarchive f
JOIN farmergroups g ON g.bsi_code = f.bsi_code AND g.crop_season = f.crop_season
WHERE f.crop_season = #cropseason
But that's just guessing. Only you know how the tables are really related, what their rows represent, what columns make a row unique and what result you are actually after. Why do you join farmergroups at all? It looks like you are not really using the table in your query.
I am still very new to SQL and Tableau however I am trying to work myself towards achieving a personal project of mine.
Table A; shows a table which contains the defect quantity per product category and when it was raised
+--------+-------------+--------------+-----------------+
| Issue# | Date_Raised | Category_ID# | Defect_Quantity |
+--------+-------------+--------------+-----------------+
| PCR12 | 11-Jan-2019 | Product#1 | 14 |
| PCR13 | 12-Jan-2019 | Product#1 | 54 |
| PCR14 | 5-Feb-2019 | Product#1 | 5 |
| PCR15 | 5-Feb-2019 | Product#2 | 7 |
| PCR16 | 20-Mar-2019 | Product#1 | 76 |
| PCR17 | 22-Mar-2019 | Product#2 | 5 |
| PCR18 | 25-Mar-2019 | Product#1 | 89 |
+--------+-------------+--------------+-----------------+
Table B; shows the consumption quantity of each product by month
+-------------+--------------+-------------------+
| Date_Raised | Category_ID# | Consumed_Quantity |
+-------------+--------------+-------------------+
| 5-Jan-2019 | Product#1 | 100 |
| 17-Jan-2019 | Product#1 | 200 |
| 5-Feb-2019 | Product#1 | 100 |
| 8-Feb-2019 | Product#2 | 50 |
| 10-Mar-2019 | Product#1 | 100 |
| 12-Mar-2019 | Product#2 | 50 |
+-------------+--------------+-------------------+
END RESULT
I would like to create a table/bar chart in tableau that shows that Defect_Quantity/Consumed_Quantity per month, per Category_ID#, so something like this below;
+----------+-----------+-----------+
| Month | Product#1 | Product#2 |
+----------+-----------+-----------+
| Jan-2019 | 23% | |
| Feb-2019 | 5% | 14% |
| Mar-2019 | 89% | 10% |
+----------+-----------+-----------+
WHAT I HAVE TRIED SO FAR
Unfortunately i have not really done anything, i am struggling to understand how do i get rid of the duplicates upon joining the tables based on Category_ID#.
Appreciate all the help I can receive here.
I can think of doing left joins on both product1 and 2.
select to_char(to_date(Date_Raised,'d-mon-yyyy'),'mon-yyyy')
, (p2.product1 - sum(case when category_id='Product#1' then Defect_Quantity else 0 end))/p2.product1 * 100
, (p2.product2 - sum(case when category_id='Product#2' then Defect_Quantity else 0 end))/p2.product2 * 100
from tableA t1
left join
(select to_char(to_date(Date_Raised,'d-mon-yyyy'),'mon-yyyy') Date_Raised
, sum(Comsumed_Quantity) as product1 tableB
where category_id = 'Product#1'
group by to_char(to_date(Date_Raised,'d-mon-yyyy'),'mon-yyyy')) p1
on p1.Date_Raised = t1.Date_Raised
left join
(select to_char(to_date(Date_Raised,'d-mon-yyyy'),'mon-yyyy') Date_Raised
, sum(Comsumed_Quantity) as product2 tableB
where category_id = 'Product#2'
group by to_char(to_date(Date_Raised,'d-mon-yyyy'),'mon-yyyy')) p2
on p2.Date_Raised = t1.Date_Raised
group by to_char(to_date(Date_Raised,'d-mon-yyyy'),'mon-yyyy')
By using ROW_NUMBER() OVER (PARTITION BY ORDER BY ) as RN, you can remove duplicate rows. As of your end result you should extract month from date and use pivot to achieve.
I would do this as:
select to_char(date_raised, 'YYYY-MM'),
(sum(case when product = 'Product#1' then defect_quantity end) /
sum(case when product = 'Product#1' then consumed_quantity end)
) as product1,
(sum(case when product = 'Product#2' then defect_quantity end) /
sum(case when product = 'Product#2' then consumed_quantity end)
) as product2
from ((select date_raised, product, defect_quantity, 0 as consumed_quantity
from a
) union all
(select date_raised, product, 0 as defect_quantity, consumed_quantity
from b
)
) ab
group by to_char(date_raised, 'YYYY-MM')
order by min(date_raised);
(I changed the date format because I much prefer YYYY-MM, but that is irrelevant to the logic.)
Why do I prefer this method? This will include all months where there is a row in either table. I don't have to worry that some months are inadvertently filtered out, because there are missing production or defects in one month.
I request your collaboration because pivot on a table and separating the records by null, but still leave the fields at 0 with NVL
Table
product | value
----------+-------
Shirts | 1200
Caps | 0
Stocks | 0
Glasses | 100
Shoes | 0
Código pivot
select * from products
PIVOT (sum(value)
for titles in ('product', 'value')) AS pivot_product
Result:
product | Shirts | Caps | Stocks | Glasses | Shoes
---------+-----------+--------+-------------+---------+----------
value | NULL | NULL | NULL | 100 | NULL
value | 1200 | NULL | NULL | NULL | NULL
Expected result:
product | Shirts | Caps | Stocks | Glasses | Shoes
---------+-----------+--------+-------------+-------+----------
valor | 1200 | NULL | NULL | 100 | NULL
Optional
product | Shirts | Caps | Stocks | Glasses | Shoes
---------+-----------+--------+-------------+-------+----------
valor | 1200 | 0 | 0 | 100 | 0
You need to put the column values in the pivot list:
Oracle Setup:
CREATE TABLE test_data ( product, value ) AS
SELECT 'Shirts', 1200 FROM DUAL UNION ALL
SELECT 'Caps', 0 FROM DUAL UNION ALL
SELECT 'Stocks', 0 FROM DUAL UNION ALL
SELECT 'Glasses', 100 FROM DUAL UNION ALL
SELECT 'Shoes', 0 FROM DUAL
Query:
SELECT 'value' AS product,
p.*
FROM test_data
PIVOT ( SUM( value ) FOR product IN (
'Shirts' AS Shirts,
'Caps' AS Caps,
'Stocks' AS Stocks,
'Glasses' AS Glasses,
'Shoes' AS Shoes
) ) p
Output:
PRODUCT | SHIRTS | CAPS | STOCKS | GLASSES | SHOES
:------ | -----: | ---: | -----: | ------: | ----:
value | 1200 | 0 | 0 | 100 | 0
db<>fiddle here
Just use conditional aggregation. It is more flexible:
select 'valor' as product,
sum(case when product = 'Shirts' then value end) as shirts,
sum(case when product = 'Caps' then value end) as caps,
sum(case when product = 'Stocks' then value end) as stockes,
sum(case when product = 'Shirts' then value end) as shirts,
sum(case when product = 'Glasses' then value end) as glasses,
sum(case when product = 'Shoes' then value end) as shoes
from test_data;
I have grouped sales from a sales view with sales below using
Select id, name, Count(*) as [Sales], product, amount
from vwSales
Group by
id,name, product, amount
ID | Name | Sales | Product | Amount
1 | Bob | 4 | Fridge | 40
1 | Bob | 12 | Washer | 120
2 | Anne | 5 | Fridge | 50
2 | Anne | 4 | Washer | 40
Is it possible to group these in to one row without using a join? So table looks something like
ID | Name | Fridge Sales | fridge Amt | Washer sales | washer amt
1 | Bob | 4 | 40 | 12 | 120
2 | Anne | 5 | 50 | 4 | 40
You can do conditional aggregation :
select id, name,
sum(case when Product = 'Fridge' then 1 else 0 end) as [Fridge Sales],
sum(case when Product = 'Fridge' then Amount else 0 end) as [fridge Amt],
sum(case when Product = 'Washer' then 1 else 0 end) as [Washer Sales],
sum(case when Product = 'Washer' then Amount else 0 end) as [Washer Amt]
from vwSales
Group by id, name;
I am trying to use the case statement on one column and calculate an aggregate of another column if the condition is satisfied.
A sample data table (for a hotel, for example) that I am querying looks like so:
| date | customer_name | customer_id | expense_type | billed_amount |
| 08-02 | John Doe | 1 | personal | 120.00 |
| 08-03 | John Doe | 1 | personal | 80.00 |
| 08-04 | John Doe | 1 | corporate | 205.00 |
| 08-02 | Adam Smith | 2 | corporate | 400.00 |
| 08-03 | Adam Smith | 2 | personal | 300.00 |
| 08-06 | Adam Smith | 2 | corporate | 150.00 |
Below is the SQL query I wrote:
Select
customer_name
, customer_id
, case when expense_type = 'personal' then sum(billed_amount) else 0 end as personal_bill
, case when expense_type = 'corporate' then sum(billed_amount) else 0 end as corporate_bill
From payments
Where date > '08-01'
Group by 1, 2
The error message I get is:
Column "expense_type" must appear in the GROUP BY clause or be used in an aggregate function
When I try to group column 3 as well (along with 1 and 2), I get this error message:
Aggregates not allowed in GROUP BY clause
Finally, below illustrates the desired resulting table:
| customer name | customer_id | personal_bill | corporate_bill |
| John Doe | 1 | 200.00 | 205.00 |
| Adam Smith | 2 | 300.00 | 550.00 |
One solution I can think of is creating two different subqueries that constrain the "expense_type" in the Where section (i.e. where expense_type = 'personal') and then querying them in the master query, but that's a lot of similar code with one line of difference. Could you please help me write this query in an efficient manner? Thank you!
Use filter:
select
customer_name,
customer_id,
sum(billed_amount) filter (where expense_type = 'personal') as personal_bill,
sum(billed_amount) filter (where expense_type = 'corporate') as corporate_bill
from payments
where date > '08-01'
group by 1, 2
customer_name | customer_id | personal_bill | corporate_bill
---------------+-------------+---------------+----------------
Adam Smith | 2 | 300.00 | 550.00
John Doe | 1 | 200.00 | 205.00
(2 rows)
Sum the entire CASE expression:
select
customer_name,
customer_id,
sum(case when customer_type = 'personal'
then billed_amount else 0 end) as personal_bill
sum(case when customer_type = 'corporate'
then billed_amount else 0 end) as corporate_bill
from payments
where date > '08-01'
group by 1, 2
Try This :-
Select MIN(customer_name) AS customer_name, customer_id,
SUM(case when expense_type = 'personal' then billed_amount::double precision else 0 end) as personal_bill,
SUM(case when expense_type = 'corporate' then billed_amount::double precision else 0 end) as corporate_bill
From payments
Where date > '08-01'
Group by customer_id
ORDER BY customer_id;