SQL value changes - sql

Suppose i have a table t1:
Product id
Price
1
p1
1
p2
2
p1
2
p1
2
p1
What i want to know is, for a unique product id i want to see if there has been a new price.
Only having constant values for a specific product id is of not interest.
Would appreciate some tips.
Best regards
My thoughts is something like "Select the product ids' where its distinct(count(price)) > 1. This should give me only the products ids that has a price change?
If a product id only has the same price without a change, it's of no interest to select.

Your idea to check COUNT(DISTINCT price > 1) is correct.
We just need to use GROUP BY with a HAVING clause:
SELECT product_id
FROM t1
GROUP BY product_id
HAVING COUNT (DISTINCT price) > 1
ORDER BY product_id;
If also the different prices should be shown, this query can be used as subquery. For example:
SELECT product_id, price
FROM t1
WHERE product_id IN
(SELECT product_id
FROM t1
GROUP BY product_id
HAVING COUNT (DISTINCT price) > 1)
ORDER BY product_id;
Or we could use JOIN rather than IN which could have a better performance:
SELECT t1.product_id, t1.price
FROM t1
JOIN
(SELECT product_id
FROM t1
GROUP BY product_id
HAVING COUNT (DISTINCT price) > 1) sub
ON t1.product_id = sub.product_id
ORDER BY t1.product_id;
We can try out these queries here: db<>fiddle

From Oracle 12, you can use MATCH_RECOGNIZE to perform row-by-row pattern matching:
SELECT product_id
FROM t1
MATCH_RECOGNIZE(
PARTITION BY product_id
ORDER BY price
PATTERN (^ first_price+ second_price)
DEFINE
first_price AS price = FIRST(price),
second_price AS price != FIRST(price)
);
Which, for the sample data:
CREATE TABLE t1 ( Product_id, Price ) AS
SELECT 1, 'p1' FROM DUAL UNION ALL
SELECT 1, 'p2' FROM DUAL UNION ALL
SELECT 2, 'p1' FROM DUAL UNION ALL
SELECT 2, 'p1' FROM DUAL UNION ALL
SELECT 2, 'p1' FROM DUAL;
Outputs:
PRODUCT_ID
1
If you want the two prices then:
SELECT product_id, price1, price2
FROM t1
MATCH_RECOGNIZE(
PARTITION BY product_id
ORDER BY price
MEASURES
FIRST(price) AS price1,
LAST(price) AS price2
PATTERN (^ first_price+ second_price)
DEFINE
first_price AS price = FIRST(price),
second_price AS price != FIRST(price)
);
Which outputs:
PRODUCT_ID
PRICE1
PRICE2
1
p1
p2
fiddle

Related

compare suppliers from 2 tables in SQL Oracle

I have 2 tables
in both tables are suppliers with items. In the table supplier_with_awards are suppliers which can deliver an item. for 1 item there could be several suppliers. in the table suppliers_with_incomming_goods are suppliers that actually supply the items. There is a situation that non-awarded supplier supplies an item.
case 1
I need to check if the item is in both tables and pick up only those with different suppliers
case 2
same as case 1 but pick up non-awarded suppliers as well.
my data
CREATE TABLE suppliers_with_awards ( supplier, item ) AS
SELECT 'supplier1', 'item1' FROM DUAL UNION ALL
SELECT 'supplier2', 'item1' FROM DUAL UNION ALL
SELECT 'supplier3', 'item2' FROM DUAL UNION ALL
SELECT 'supplier4', 'item3' FROM DUAL ;
CREATE TABLE suppliers_with_incoming_goods ( supplier, item ) AS
SELECT 'supplier1', 'item1' FROM DUAL UNION ALL
SELECT 'supplier2', 'item1' FROM DUAL UNION ALL
SELECT 'supplier5', 'item2' FROM DUAL UNION ALL
SELECT 'supplier6', 'item4' FROM DUAL ;
with simple join we get for item1 unnecessary combinations supplier1-supplier2 and vica versa but in reality supplier1 got award and supplier1 delivers, the same goes for supplier2. So I used row_number to exclude such cross combo if you have better solution let me know.
with award as (
select supplier, item, row_number() over (partition by item order by supplier) r
from suppliers_with_awards
),
goods as (
select supplier, item, row_number() over (partition by item order by supplier) r
from suppliers_with_incoming_goods
)
select a.supplier,a.item,g.supplier from award a join goods g on a.item=g.item and a.r=g.r and a.supplier<>g.supplier;
SUPPLIER ITEM SUPPLIER
supplier3 item2 supplier5
this query finds the item2 because there are different suppliers as I want (case 1).again if there is better solution for this , please ....
But I need somehow to get the non-awarded supplier6 with item4 as well (case 2)
thanks
The current query may not return all results with inserted different values such as supplier7 and supplier8 for item1 inserted into the table suppliers_with_awards. I don't recommend use analytic function in this case, rather you can convert the query into the following which includes NOT EXISTS. And use UNION ALL, since you may need to return more than two supplier which already should be independently listed each unique one into one seperate line
--# Case 1
WITH item_supplier AS
(
SELECT g.item AS item,
a.supplier AS supplier_a,g.supplier AS supplier_g
FROM suppliers_with_awards a
JOIN suppliers_with_incoming_goods g
ON a.item = g.item
)
SELECT DISTINCT item, supplier_a AS supplier
FROM item_supplier i
WHERE NOT EXISTS ( SELECT 0
FROM item_supplier
WHERE supplier_g = i.supplier_a)
UNION ALL
SELECT DISTINCT item, supplier_g
FROM item_supplier i
WHERE NOT EXISTS ( SELECT 0
FROM item_supplier
WHERE supplier_a = i.supplier_g)
for the second case just convert the INNER JOIN to RIGHT or FULL JOIN, and filter out the NULL values of item and supplier in the main query such as
--# Case 2
WITH item_supplier AS
(
SELECT g.item AS item,
a.supplier AS supplier_a,g.supplier AS supplier_g
FROM suppliers_with_awards a
RIGHT JOIN suppliers_with_incoming_goods g
ON a.item = g.item
), its AS
(
SELECT DISTINCT item, supplier_a as supplier
FROM item_supplier i
WHERE NOT EXISTS ( SELECT 0
FROM item_supplier
WHERE supplier_g = i.supplier_a)
UNION ALL
SELECT DISTINCT item, supplier_g
FROM item_supplier i
WHERE NOT EXISTS ( SELECT 0
FROM item_supplier
WHERE supplier_a = i.supplier_g)
)
SELECT *
FROM its
WHERE item IS NOT NULL
AND supplier IS NOT NULL
Demo

Adding new ROWID to SQLite SELECT statement based on the UNION of several tables

Consider the following SQLite statement where each product_id may appear at least once in each table. The purpose of our SELECT statement being to merge all the product_id's into one table.
SELECT product_id
FROM product_id_table_UK
UNION
SELECT product_id
FROM product_id_table_DE
UNION
SELECT product_id
FROM product_id_table_ES
UNION
SELECT product_id
FROM product_id_table_IT
UNION
SELECT product_id
FROM product_id_table_FR
How can one add an ID INTEGER column to the resulting view?
Each table has it's own ID INTEGER column but if we SELECT ID column then we will have duplicate product_id columns in the new view with separate ID's.
Addding ROWID doesn't work since it returns the IDs of the tables.
Furthermore views do not have rows stored in them.
This is painful in older versions of SQLite (pre-row_number() days). You can do:
with p as (
SELECT product_id
FROM product_id_table_UK
UNION
SELECT product_id
FROM product_id_table_DE
UNION
SELECT product_id
FROM product_id_table_ES
UNION
SELECT product_id
FROM product_id_table_IT
UNION
SELECT product_id
FROM product_id_table_FR
)
select p.*,
(select count(*) from p p2 where p2.product_id <= p.product_id)
from p;

Select Customer ID who hasnt purchased product X

I have a table of customer IDs and Products Purchased. A customer ID can purchase multiple products over time.
customerID, productID
In BigQuery I need to find the CustomerID for those who have not purchased product A.
I've been going around in circles trying to do self joins, inner joins, but I'm clueless.
Any help appreciated.
select customerID
from your_table
group by customerID
having sum(case when productID = 'A' then 1 else 0 end) = 0
and to check if it only contains a name
sum(case when productID contains 'XYZ' then 1 else 0 end) = 0
Below is for BigQuery Standard SQL
#standardSQL
SELECT CustomerID
FROM `project.dataset.yourTable`
GROUP BY CustomerID
HAVING COUNTIF(Product = 'A') = 0
You can test / play with it using dummy data as below
#standardSQL
WITH `project.dataset.yourTable` AS (
SELECT 1234 CustomerID, 'A' Product UNION ALL
SELECT 11234, 'A' UNION ALL
SELECT 4567, 'A' UNION ALL
SELECT 7896, 'C' UNION ALL
SELECT 5432, 'B'
)
SELECT CustomerID
FROM `project.dataset.yourTable`
GROUP BY CustomerID
HAVING COUNTIF(Product = 'A') = 0
how would I adjust this so it could be productID contains "xyz"
#standardSQL
WITH `project.dataset.yourTable` AS (
SELECT 1234 CustomerID, 'Axyz' Product UNION ALL
SELECT 11234, 'A' UNION ALL
SELECT 4567, 'A' UNION ALL
SELECT 7896, 'Cxyz' UNION ALL
SELECT 5432, 'B'
)
SELECT CustomerID
FROM `project.dataset.yourTable`
GROUP BY CustomerID
HAVING COUNTIF(REGEXP_CONTAINS(Product, 'xyz')) = 0
If you have a customer table, you might want:
select c.*
from customers c
where not exists (select 1 from t where t.customer_id = c.customer_id and t.proectID = 'A');
This will return customers who have made no purchases as well as those who have purchased all but product A. Of course, the definition of a customer in your data might be that the customer has made a purchase, in which case I like Juergen's solution.

explode tables in sql

Problem statement:
I have Product_id and Quantity column in my table
Say product_id A has 2 units, B has 3 units etc
How do i make a table with 2 rows of Product_id A, 3 rows of Product_id B?
I did this exercise based on a recursive Common Table Expression:
;WITH CTE (Vals)
AS (
SELECT 1
UNION ALL
SELECT 1 + Vals
FROM CTE WHERE Vals< 99
)
SELECT Product_id
FROM Mytable A
INNER JOIN CTE C ON C.Vals <= A.Quantity
ORDER BY A.Product_id
supposing Quantity < 99
could help you
This worked for me, tested on your example :
WITH tally(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM tally WHERE n<100)
SELECT Product_id, 1 as Quantity
FROM MyTable CROSS JOIN tally
WHERE n<=quantity
ORDER BY Product_id;

Pull all records only if 3 dups or more exist

I'm trying to pull ALL fields and records if two of the fields (category and measure) create 3 or more dups.
SELECT category
,measure
,date
FROM my_table
for example:
category measure date
EVENTS COL 04/15/2014
EVENTS COL 05/21/2014
EVENTS COL 07/16/2014
So the above meets the criteria of 3 or more so we would pull all three.
category measure rec_count
EVENTS COL 3
I think you want something like - gives you all rows with 3 or more records
SELECT
category,
measure,
date
FROM
my_table t1
inner join (
SELECT
category,
measure
FROM
my_table
group by
category,
measure
having
count(*) >= 3
) t2 on
t2.category = t1.category and
t2.measure = t1.measure
To get only the count you would run
SELECT
category,
measure
count(*)
FROM
my_table
group by
category,
measure
having
count(*) >= 3
Sounds like you're looking for GROUP BY:
SELECT category
,measure
,count(*) as rec_count
FROM my_table
GROUP BY category, measure
HAVING count(*) >= 3
SELECT
category, measure, date, count(*) cnt
FROM my_table
GROUP BY category, measure
HAVING cnt >= 3
;
This will not group your results.
SELECT q.* FROM
(SELECT
category
,measure
,date
,COUNT(*) OVER(PARTITION BY category,measure) [rec_count]
FROM
my_table) q
WHERE q.rec_count >= 3