SQL QUERY to count repeats with 2 conditions - sql

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

Sum by Groups Before and after appear Number in column SQL

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

Calculate lead-time between selected rows (SQL)

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

Conditional Order by based on aggregate value of a column in Oracle Sql Developer

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;

Organizing SQL data based on date

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 for Repeated category(count items in each category) and has to contain atleast one specific type

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.