To find repeated items only when when it satisfies two conditions. In this example count repeats of item type for each customer_id only when it has order size "Big" and its corresponding date is before other instances. This first condition and repeats can be achieved by using this code.
Select Customer_id, Item_Type, COUNT(*)
from table
group by Customer_id, Item_Type
having count(*) > 1 and sum(case when Order_Size = 'Big' then 1 else 0 end) > 0;
how do I include date aspect as well to this?
I would do this as:
select t.customer_id, t.item_type, count(*)
from (select t.*,
min(case when OrderSize = 'Big' then date end) over (partition by customer_id, item_type) as min_big
from t
) t
where date > min_big
group by t.customer_id, t.item_type;
I believe you could use a window function in a subquery to decide which rows to count, then count them in your main query. Something like:
Select
customer_id, item_type, sum(count_pass) as Count
FROM
(
Select Customer_id,
Item_Type,
CASE
WHEN Order_Size = 'Big' THEN 0
WHEN MIN(Order_Size) OVER (PARTITION BY Customer_ID, Item_Type ORDER BY DateField ASC ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) = 'BIG' THEN 1
ELSE 0
END as count_pass
FROM table
) subqry
GROUP BY 1,2
That big case statement breaks down like:
If this record is 'Big' then ignore it
If you order all the records by date for each group of customer_id/item_type and look at all the records that precede this record, and the min(order_size) in that group of records (sorted lexicographically) is 'Big' then you have a preceding date with big and can count this record
Otherwise... you can't count it. Which would just be records with order_size='small' without a preceding 'big'.
Related
I have a group of records by id ordered by date ('Date'), which I want to sum up the amounts or, in 2 groups, 1 before any number appears ('Condition'), and another group after the first number,It doesn't matter if a 0 appears after a number, add before any number appears, and add after.
You can use conditional aggregation. For example:
select
id,
sum(case when s = 0 then amount else 0 end) as amount_before,
sum(case when s <> 0 then amount else 0 end) as amount_after
from (
select t.*,
sum(abs(condition)) over(partition by id order by date) as s
from t
) x
group by id
Given this table where we have users, the product that they used, and the first date that they used the product (I have also created a simple rank by user window). Note, each user will only have minimum 0 rows if they used nothing before, and 2 rows, if they used both products. There are only 3 products - cigars and beers.
How can I create a new view where each row is 1 user, the next column shows the first product, the next column shows the 2nd product, and the last column shows the lead-time b/w the first dates of use?
One method is conditional aggregation with row_number():
select user,
max(case when seqnum = 1 then product end) as product_1,
max(case when seqnum = 2 then product end) as product_2,
(max(case when seqnum = 2 then time_used end) -
max(case when seqnum = 1 then time_used end)
) as dif
from (select t.*,
row_number() over (partition by user order by time_used) as seqnum
from t
) t
group by user;
Date/time functions vary significantly across different databases. Not all support a simple -, so you might nee to adjust for your database.
Minus between dates may not work on each database
select
c1.user_id,
c1.first_product_used,
c2.second_product_used,
COALESCE(CAST((Cast(c2.second_date AS DATE) - Cast(c1.first_date AS DATE)) AS VARCHAR(20)), 'n/a') AS "leadtime_days"
from
(
select
user_id,
product_used AS first_product_used,
time_used AS first_date
from
check2
where
rank_of_use = 1
)c1
LEFT OUTER JOIN
(
select
user_id,
product_used AS second_product_used,
time_used AS second_date
from
check2
where
rank_of_use = 2
)c2
ON
c1.user_id = c2.user_id
I have a table that has ID, CW_savings, PW_Savings and Savings_Diff(CW-PW). I want to order the table depending on the sum(Savings_Diff), if it is >=0 then Desc(greatest positive diff shown first) else asc(greatest -ve diff shown first). Currently i am using :
select ID, CW_savings, PW_Savings,Savings_Diff
from table
order by case
when sum(savings_diff) >=0 then Savings_diff end DESC
when sum(savings_diff) < 0 then SAvings_diff end ASC
;
This query is not working because i believe you can't use aggregate functions in Order by clause.
Looking for a workaround on this?
If the sum that you want to check is the total sum of the column Savings_Diff of all the rows of the table then you can do this:
select t.ID, t.CW_savings, t.PW_Savings, t.Savings_Diff
from tablename t
cross join (select sum(Savings_Diff) total from table) s
order by -sign(s.total) * t.Savings_diff
When the sum is positive then the sorting is equivalent to:
order by -Savings_diff
or
order by Savings_diff DESC
When the sum is negative then the sorting is equivalent to:
order by Savings_diff
or
order by Savings_diff ASC
I am going to speculate that id has multiple rows in the table and you want the sum for the id. You can use window functions:
select ID, CW_savings, PW_Savings,Savings_Diff
from table
order by (case when sum(savings_diff) over (partition by id) >= 0 then Savings_diff end) desc,
(case when sum(savings_diff) over (partition by id) < 0 then savings_diff end) ASC;
I am trying to organize my SQL data based off of the dates from which the orders were made.
My data:
SELECT DISTINCT ORDER_NO, ITEM, VERSION_NO,
(CASE WHEN ROW_NUMBER() OVER (PARTITION BY ORDER_NO ORDER BY NOT_BEFORE_DATE
ASC) = 1
THEN 'what-if'
ELSE 'wh'
END) AS VERSION_NEW
,
(CASE WHEN ROW_NUMBER() OVER (PARTITION BY ORDER_NO ORDER BY
NOT_BEFORE_DATE ASC) = 2
THEN 'initial'
ELSE 'other'
END) AS VERSION
FROM FDT_MAPTOOL
WHERE ITEM IN (1032711)
;
My results:
I want my data to be ordered by PO# and the date it was created.
As you can see in my picture the First two line have the same ITEM and same PO (Order_No). I need the first two to say Initial on the side because they are the first two based on the dates. They were created first. Everything after should say other.
I am not sure if PL/SQL is needed for this?
Thank you!
Use a different analytic function so that more than one row can have the value of 1 e.g.
SELECT DISTINCT ORDER_NO, ITEM, VERSION_NO,
(CASE WHEN DENSE_RANK() OVER (PARTITION BY ORDER_NO ORDER BY NOT_BEFORE_DATE
ASC) = 1
THEN 'what-if'
ELSE 'wh'
END) AS VERSION_NEW
,
(CASE WHEN DENSE_RANK() OVER (PARTITION BY ORDER_NO ORDER BY
NOT_BEFORE_DATE ASC) = 1
THEN 'initial'
ELSE 'other'
END) AS VERSION
FROM FDT_MAPTOOL
WHERE ITEM IN (1032711)
;
Either rank() OR dense_rank() should work here instead of row_number()
nb: note sure if you really need "select distinct"
SQL Query to perform repeated item_id against each category_id. For this the query would be
Select item_id, category_id, COUNT(*)
from table
group by category_id, item_id
having count(*)>1
but how can I change the code to filter only ones which have atleast one type A in their repeated counts.
You can count the "A"s in the HAVING clause as well:
Select item_id, category_id, COUNT(*)
from table
group by category_id, item_id
having count(*) > 1 and sum(case when type = 'A' then 1 else 0 end) > 0;
Given your sample data, you can shorten this to:
Select item_id, category_id, COUNT(*)
from table
group by category_id, item_id
having count(*) > 1 and min(type)= 'A' ;
But this depends on the actual names you are using.