sql not in and in oporators - sql

HI I am unable to filter materials that belong to only specific list.
Select material
from price
where region='04'
and pricelist ='5'
and pricelist not in ('4','6','7');
I want only those materials which are unique only in pricelist 5 and not in any other pricelists. how can i get this?

You might try something like this (alternately, you could do a self-join, but I think this is just as easy, if not easier, to understand):
SELECT material
FROM price a
WHERE region = '04'
AND pricelist = '5'
AND NOT EXISTS ( SELECT 1 FROM price b
WHERE b.material = a.material
AND b.region = a.region
AND b.pricelist != a.pricelist )
What the above will do will find values of material in region '04' where the pricelist is '5' while excluding those same materials in the same region but on a different price list.

Your current query will only return results where pricelist = 5 -- the not in statement is irrelevant unless it exclude that record. It sounds like you want to return any material which matches on the 5, but doesn't have any other non-5 matches.
One option is to use exist. Here's another option using conditional aggregation with max and case which eliminates the need for multiple queries:
select material
from (
select material,
max(case when pricelist = '5' then 1 else 0 end) haspl5,
max(case when pricelist != '5' then 1 else 0 end) hasothers
from price
where region='04'
group by material
) t
where haspl5 = 1 and hasothers != 1
SQL Fiddle Demo

Related

Is there a way to collect the data and inspect in one pass using groupby function

Sample Data of table_1
Have this Query that returns
select
customer,
SUM(CASE WHEN activity IN ( 'a','b')
THEN 1
ELSE 0 END) AS num_activity_a_or_b
from table_1
group by customer
Results:
Want to extend this to return one more column if for a given code say X1 if the Activity is "a" and "c" then return num_of_a_and_c_activity.
A bit stuck how to collect and inpect the code and activities in one pass.
can we combine windowing function to achieve this.
Please advise and help
UPDATE:
based on the updated results, maybe the below query is what you need
So what i assume is that you need both a and c as well x1 .
So I count distinct activities which are a and c and then do integer division by 2. if only a is present then count distinct =1 but 1/2 =0 in integer division.
It is only 1 when both a and c are present.
select
customer,
SUM(CASE WHEN activity IN ( 'a','b')
THEN 1
ELSE 0
END) AS num_activity_a_or_b,
COUNT(DISTINCT CASE WHEN code IN ('x1') AND activity IN ( 'a','c')
THEN activity
ELSE NULL
END)/2 AS num_activity_a_and_c
from table_1
group by customer
Maybe your query can be
select
customer,
SUM(CASE WHEN activity IN ( 'a','b')
THEN 1
ELSE 0
END) AS num_activity_a_or_b,
SUM(CASE WHEN code IN ('x1') AND activity IN ( 'a','c')
THEN 1
ELSE 0
END) AS num_activity_a_or_c
from table_1
group by customer

Is there a way to join a numerical column to a alphabetical column in sql?

I have a table of data which contains user actions such as 'buy' and 'sell' in a column and a separate column which contains the quantity traded for the movement. Is there a way to connect the two columns that would allow me to filter results where, for example, the quantity of the brought good is greater than the quantity of the sold goods.
Example of table
Action
Quantity
Product
Buy
10
abc
Sell
9
abc
short
11
xyz
cover
11
xyz
Thanks in advance.
There might be way more efficient ways to do this, but you can pretty easily join them together as subqueries.
SELECT
product
FROM
(
SELECT * FROM table WHERE action = 'Buy'
) as bought
JOIN
(
SELECT * FROM table WHERE action = 'Sell'
) as sold
ON
bought.quantity > sold.quantity
AND
bought.product = sold.product
Try something like this:
select product.name as product ,
coalesce( bought.quantity , 0 ) as bought ,
coalesce( sold.quantity , 0 ) as sold ,
coalesce( short.quantity , 0 ) as shorted ,
coalesce( covered.quantity , 0 ) as covered
from ( select distinct
t.Product as name
from my_table t
) as product
left join ( select t.Quantity as quantity
from my_table t
where action = 'Buy'
) as bought on bought.product = product.name
left join ( select t.Quantity as quantity
from my_table t
where action = 'Sell'
) sold on sold.product = product.name
left join ( select t.Quantity as quantity
from my_table t
where action = 'short'
) shorted on shorted.product = product.name
left join ( select t.Quantity as quantity
from my_table t
where action = 'cover'
) covered on covered.product = product.name
That should give you a flat result set with 1 row per product, transforming this:
Action
Quantity
Product
Buy
10
abc
Sell
9
abc
short
11
xyz
cover
11
xyz
Into this:
product
bought
sold
shorted
covered
abc
10
9
0
0
xyz
0
0
11
11
Once you have flattened your table, you can filter it however you like with a simple where clause, for instance:
select product.name as product ,
coalesce( bought.quantity , 0 ) as bought ,
coalesce( sold.quantity , 0 ) as sold ,
coalesce( short.quantity , 0 ) as shorted ,
coalesce( covered.quantity , 0 ) as covered
from ( select distinct
t.Product as name
from my_table t
) as product
left join ( select t.Quantity as quantity
from my_table t
where action = 'Buy'
) as bought on bought.product = product.name
left join ( select t.Quantity as quantity
from my_table t
where action = 'Sell'
) sold on sold.product = product.name
left join ( select t.Quantity as quantity
from my_table t
where action = 'short'
) shorted on shorted.product = product.name
left join ( select t.Quantity as quantity
from my_table t
where action = 'cover'
) covered on covered.product = product.name
where bought.quantity > sold.quantity
OR short.quantity > cover.quantity
Another way to get to [arguably] the same result is via group by (but much depends on the cardinality of the data):
select t.product as product,
sum(case t.action when 'Buy' then t.Quantity else 0 end) as bought,
sum(case t.action when 'Sold' then t.Quantity else 0 end) as sold,
sum(case t.action when 'short' then t.Quantity else 0 end) as shorted,
sum(case t.action when 'cover' then t.Quantity else 0 end) as covered
from my_table t
group by t.product
having sum(case t.action when 'Buy' then t.Quantity else 0 end)
> sum(case t.action when 'Sold' then t.Quantity else 0 end)
OR sum(case t.action when 'short' then t.Quantity else 0 end)
> sum(case t.action when 'cover' then t.Quantity else 0 end)
You'll note that with the group by, the filter moves from the where clause to the having clause. That's because the conceptual order of operations for a select statement is
Compute the full, cartesian join of all tables in the from clause.
Apply the join criteria to filter the results set.
Filter that by the criteria in the where clause.
If a group by clause exists, reduce the results set to to one row for each distinct combination of values in the grouping columns and compute the value of any aggregate functions.
Filter that summary result set by the criteria in the having clause.
Finally, if an order by clause exists, order the results set accordingly [without an order by clause, order is not guaranteed, even between 2 subsequent executions of the same exact select statement.]
Some assumptions about your table structure: each product has exactly 4 rows, one for each action, never more or less. You have no time or actionId.
This would get you a view of the data that will be more intuitive for you to work from. If you're still in the design stages, I'd consider changing this table to be more like this anyway, OR update the table to have one row per action (per sale of that product, rather than for all sales of that product).
SELECT
product, action,
buy.quantity AS bought, sell.quantity AS sold,
short.quantity AS shorts, cover.quantity AS covers
FROM
table_name AS buy
JOIN
table_name AS sell ON buy.product = sell.product
JOIN
table_name AS short ON buy.product = short.product
JOIN
table_name AS cover ON buy.product = cover.product
WHERE
buy.action = 'Buy'
AND sell.action = 'Sell'
AND short.action = 'short'
AND cover.action = 'cover';
If you restructure your table this way, or use this as a base query (or encode it as a VIEW), it should be intuitive for you to do the things you ask:
SELECT product
FROM (subquery) AS summary
WHERE bought > sold;
If your table is actually one row per action (as in, one row every time you sell) then you will need to do more work.

Oracle SQL to get min number from case statement

I have this query below (in Oracle PL/SQL) which I am trying to pull a price, it seems to give me 3 records but I only want one. I want to show the lowest rate if I run into this kind of scenario, can I possibly put a min function around the 'rate' field which is in the case statement below?
select /*+ rule */ a.skucode as skucode,a.sizecode as
sizecode,b.colourdescription as colourdesc, a.season as season,
(case when sp.eventnbr in (select eventnbr from event where sysdate
between eventbegints
and eventendts) then rate else sellprice end) as listprice
from sku a, colour b, skuprcevnt sp
where a.skucode = '00000000051361264-04'
--" and a.storecode = '00000' " +
and a.storecode = '00000'
and a.colourcode = b.colourcode
and a.skucode=sp.skucode(+)
order by a.skucode, a.sizecode, b.colourdescription;
This gives the following result (but I want to see the price of 76.99 only):
SKUCODE SIZECODE COLOURDESC SEASON LISTPRICE
00000000051361264-04 XL BLACK FA-13 155
00000000051361264-04 XL BLACK FA-13 155
00000000051361264-04 XL BLACK FA-13 76.99
You can use MIN in the column list if you group the query appropriately:
select a.skucode,
a.sizecode,
b.colourdescription as colourdesc,
a.season,
MIN(case
when sp.eventnbr in (select eventnbr
from event
where sysdate between eventbegints
and eventendts)
then rate
else sellprice
end) as listprice
from sku a
INNER JOIN colour b
ON b.colourcode = a.colourcode
LEFT OUTER JOIN skuprcevnt sp
ON sp.skucode = a.skucode
where a.skucode = '00000000051361264-04' and
a.storecode = '00000'
GROUP BY a.SKUCODE, a.SIZECODE, b.COLOURDESCRIPTION, a.SEASON
order by a.skucode, a.sizecode, b.colourdescription, a.SEASON;
Share and enjoy.
The contents of your select clause will never affect the number of rows returned by a query. You need a filter in the WHERE clause along the lines of WHERE rate = (select min(rate) sku where skucode=a.skucode ).
Your example is more complicated so the actual filter would be bigger but that's just an idea of what it would look like.

Select Categories and count the Ads inside

I want to make a query that select Categories name and count the max of Ads (active) in each category, I already did this:
SELECT
cat.cat_id,
cat.cat_desc,
COUNT(ad_id) AS cat_total_ads
FROM
tab_categories AS cat LEFT JOIN
tab_ads AS ads ON ads.cat_id = cat.cat_id
WHERE
cat_active = #cat_active AND
cat_deleted = #cat_deleted
GROUP BY
cat_desc, cat.cat_id
This query is counting the non active Ads too. How can I put the value 0 for the non active Ads?
One way to accomplish this is to use a combination of a CASE statement to determine if you want to count the item and the SUM function.
For example
SUM(CASE WHEN Active THEN 1 ELSE 0 END)
The SUM ( CASE ..... then 1 else 0 end ) as colName should do it for you !!
Try this i do not have the table to recreate to test but this or something similar should work.
select
cat.cat_id,
cat.cat_desc,
count(ad_id) as Total,
sum(case when ad_id= 0 then 1 else 0 end) as 'ad_id'
from
tab_categoriesAS cat LEFT JOIN
tab_ads AS ads ON ads.cat_id = cat.cat_id
WHERE
cat_active = #cat_active AND
cat_deleted = #cat_deleted
GROUP BY
cat_desc, cat.cat_id

SQL Nested Select statements with COUNT()

I'll try to describe as best I can, but it's hard for me to wrap my whole head around this problem let alone describe it....
I am trying to select multiple results in one query to display the current status of a database. I have the first column as one type of record, and the second column as a sub-category of the first column. The subcategory is then linked to more records underneath that, distinguished by status, forming several more columns. I need to display every main-category/subcategory combination, and then the count of how many of each sub-status there are beneath that subcategory in the subsequent columns. I've got it so that I can display the unique combinations, but I'm not sure how to nest the select statements so that I can select the count of a completely different table from the main query. My problem lies in that to display the main category and sub category, I can pull from one table, but I need to count from a different table. Any ideas on the matter would be greatly appreciated
Here's what I have. The count statements would be replaced with the count of each status:
SELECT wave_num "WAVE NUMBER",
int_tasktype "INT / TaskType",
COUNT (1) total,
COUNT (1) "LOCKED/DISABLED",
COUNT (1) released,
COUNT (1) "PARTIALLY ASSEMBLED",
COUNT (1) assembled
FROM (SELECT DISTINCT
(t.invn_need_type || ' / ' || s.code_desc) int_tasktype,
t.task_genrtn_ref_nbr wave_num
FROM sys_code s, task_hdr t
WHERE t.task_genrtn_ref_nbr IN
(SELECT ship_wave_nbr
FROM ship_wave_parm
WHERE TRUNC (create_date_time) LIKE SYSDATE - 7)
AND s.code_type = '590'
AND s.rec_type = 'S'
AND s.code_id = t.task_type),
ship_wave_parm swp
GROUP BY wave_num, int_tasktype
ORDER BY wave_num
Image here: http://i.imgur.com/JX334.png
Guessing a bit,both regarding your problem and Oracle (which I've - unfortunately - never used), hopefully it will give you some ideas. Sorry for completely messing up the way you write SQL, SELECT ... FROM (SELECT ... WHERE ... IN (SELECT ...)) simply confuses me, so I have to restructure:
with tmp(int_tasktype, wave_num) as
(select distinct (t.invn_need_type || ' / ' || s.code_desc), t.task_genrtn_ref_nbr
from sys_code s
join task_hdr t
on s.code_id = t.task_type
where s.code_type = '590'
and s.rec_type = 'S'
and exists(select 1 from ship_wave_parm p
where t.task_genrtn_ref_nbr = p.ship_wave_nbr
and trunc(p.create_date_time) = sysdate - 7))
select t.wave_num "WAVE NUMBER", t.int_tasktype "INT / TaskType",
count(*) TOTAL,
sum(case when sst.sub_status = 'LOCKED' then 1 end) "LOCKED/DISABLED",
sum(case when sst.sub_status = 'RELEASED' then 1 end) RELEASED,
sum(case when sst.sub_status = 'PARTIAL' then 1 end) "PARTIALLY ASSEMBLED",
sum(case when sst.sub_status = 'ASSEMBLED' then 1 end) ASSEMBLED
from tmp t
join sub_status_table sst
on t.wave_num = sst.wave_num
group by t.wave_num, t.int_tasktype
order by t.wave_num
As you notice, I don't know anything about the table with the substatuses.
You can use inner join, grouping and count to get your result:
suppose tables are as follow :
cat (1)--->(n) subcat (1)----->(n) subcat_detail.
so the query would be :
select cat.title cat_title ,subcat.title subcat_title ,count(*) as cnt from
cat inner join sub_cat on cat.id=subcat.cat_id
inner join subcat_detail on subcat.ID=am.subcat_detail_id
group by cat.title,subcat.title
Generally when you need different counts, you need to use the CASE statment.
select count(*) as total
, case when field1 = "test' then 1 else 0 end as testcount
, case when field2 = 'yes' then 1 else 0 endas field2count
FROM table1