Convert multiple columns to a single integer with bitwise operation in SQL - sql

I have table with thousands of rows describing say some product. It has multiple columns about the features of the product. For e.g.
productid productname isOnSale HasVeteranDiscount IsTaxExempt Otherdata1 Otherdata2 ...
1 rice 0 1 1 info1 info2
2 camera 1 0 0 info3 info4
Another table
[Productparts]
Partid parentproductid isGeneric CanBeSoldSeperate OtherData1 Otherdata2 ...
Another table:
ProductId ItemsSold Datesold
1 23 4/20/2013
I have an enum that describes productfeature:
[Flags]
public enum ProductFeature : short
{
None = 0,
isOnSale = 0x001,
HasVeteranDiscount = 0x002,
IsTaxExempt = 0x004,
isGeneric = 0x008,
CanBeSoldSeperate = 0x010,
}
For statistical analysis I need to insert the above data from the three tables into one table as a bitwise or'ed integer of all applicable product features with the count of the products that fall into that category along with product sale counts such as:
ProductTrend
ProductFeatures ItemsSold MonthSold
For e.g if a product isonsale and has one or more part which is generic and has one or more part which can be sold seperate then its 25.
And another product hasveterandiscount and has one or more parts that can be sold seperate then its 18 [HasVeteranDiscount | CanBeSoldSeperate = 18]
My table should look like:
ProductTrend
ProductFeatures ItemsSold MonthSold
25 34 April
18 12 May
The most important part here which I need help is how do I combine the data about the product from multiple columns in multiple tables into one single integer column productFeatures with bitwise operations.

SQL Server supports | for bitwise or:
select productid
, productname
, case when isOnSale = 1 then 1 else 0 end |
case when HasVeteranDiscount = 1 then 2 else 0 end |
case when IsTaxExempt = 1 then 4 else 0 end as Flags
from Table1
Example on SQL Fiddle.

try this , sample here
select productid,intheMonthOf,features,sum(itemsold) as TotalSoldItems
from (
select a.productid,Datename(month,datesold) as intheMonthOf, itemsold,
case when a.isonsale =1 then 1 else 0 end |
case when a.hasveterrandiscount =1 then 2 else 0 end |
case when a.istaxexempt =1 then 4 else 0 end |
case when b.isgeneric =1 then 8 else 0 end |
case when b.canbesoldseparate =1 then 10 else 0 end as features
from t1 a
left outer join t2 b on a.productid=b.parentproductid
inner join t3 c on c.porductid=a.productid )main
group by productid,intheMonthOf,features

Related

Complex SQL Query - i found no answer :(

i've have a MS-SQL table which contains the following columns:
ID, SKU, StockID, StockQTY
The columns/rows are filled like:
ID
SKU
StockID
StockQTY
1
1111
1
12
2
1111
13
20
3
2222
1
0
4
2222
13
5
5
3333
1
0
6
3333
13
4
Now i need a SQL query which show all SKU (second column) which have a StockQTY = 0 in StockID = 1 AND a StockQTY > 1 in StockID 13. All other rows should not be shown (in this example row 1 and 2 should not be shown in the result).
Have anyone an idea how to realize this?
You can use a correlated subquery to test for skus that have 0 qty in StockID 1 and then just keep anything that has a qty greater than 1 in your StockID 13.
SELECT SKU
FROM yourtable yt
WHERE Stockid = 13
AND NOT EXISTS (SELECT 1 FROM yourtable WHERE StockID = 1 AND yt.SKU = SKU)
HAVING Sum(StockQty) > 1
Try something like:
SELECT SKU FROM yourTableName WHERE
(StockQTY = 0 AND StockID = 1) OR (StockQTY > 1 AND StockID = 13);
To pull data obviously it is a SELECT statement, you could use * instead of SKU if you want ALL the data instead of just the SKU number. Exchange yourTableName with the actual table name. The WHERE clause is the meat of your question. This use of OR and AND can be confusing. We have 2 cases we are testing. One where the stock is 0 but the ID is 1, but we use the OR so that regardless if that is found or not, we can then do the next part where the StockQTY is 1 and the StockID is 13. You need the and for both conditions in the own case, and you want OR so that either of these two cases will pull up.

Enquiry on Query in Oracle SQL

I have data as below:
Category | Type | Rank
Milk 1 1
Milk 2 2
Milk 3 3
Chocolate 1 2
Candy 1 1
Any idea to achieve the output of below with a flat SQL query:
Category
Milk
Query must satisfy the below conditions:
1. Only Type 1 and Rank 1 will be selected.
2. Only Category that has Type 1 and Type 2 will be selected.
In the sample data above, only Milk that satisfy the conditions mentioned above.
My query is below. But it's incorrect, because it will return Candy as well.
SELECT DISTINCT Category
FROM table
WHERE Type = 1 AND rank = 1
Thanks in advance!
You can try below -
DEMO
select distinct category
from table a
WHERE Type = 1 AND rank = 1
and exists
(select 1 from table b where a.category=b.category and type in (1,2)
group by category having count(distinct type)=2)
OUTPUT:
category
Milk
You can use aggregation:
select category
from t
group by category
having sum(case when type = 1 and rank = 1 then 1 else 0 end) > 0 and
sum(case when type = 2 then 1 else 0 end) > 0;
Assuming no duplicates, this can be simplified to:
select category
from t
where (type = 1 and rank = 1) or type = 2
group by category
having count(distinct type) = 2;

finding unique record using min and max in same query from several criteria

I have a common table expression with the following fields
product.identifier, ingredient.identifier, ingredient.cost,
ingredient.isActive, ingredient.isPrimary
I'm trying to find a record based off the following criteria among multiple records
if isActive = 1 and isPrimary = 1, choose that record
if the record with isPrimary = 1 but isActive = 0, choose the record with the highest/max cost where isPrimary = 0 and isActive = 1
if all records from step 2 have the same cost, choose the oldest/min record based off ingredient.Identifier
the logic to find these on their own is simple but combining the logic into one clause is not working as expected. here is the expected output I'm trying to match along with the incorrect SQL
product ingredient cost isActive isPrimary isChosenRecord
-- isActive and isPrimary example
1 10 1.00 1 1 yes
1 11 1.10 1 0 no
2 20 2.00 1 1 yes
2 22 2.15 1 0 no
-- primary record is inactive, choose max cost record
3 30 3.00 0 1 no
3 31 3.10 1 0 no
3 32 3.20 1 0 yes
4 40 4.00 0 1 no
4 41 4.10 1 0 no
4 42 4.20 1 0 yes
-- primary record is inactive, all records have same cost, choose oldest record
5 50 5.00 0 1 no
5 51 5.00 1 0 yes
5 52 5.00 1 0 no
6 60 6.00 0 1 no
6 61 6.00 1 0 yes
6 62 6.00 1 0 no
; with [ActiveRecordsCTE] as
(
select
ProductIdentifier = p.Identifier,
IngredientIdentifier = i.Identifier,
i.Cost, i.isActive, i.isPrimary
from Product p
inner join Ingredient i on i.Identifier = p.Identifier
where i.isActive = 1
),
[CalculatedPrimaryRecords] AS
(
SELECT
r.ProductIdentifier,
r.IngredientIdentifier
FROM ActiveRecordsCTE r
WHERE r.IsPrimary = 1
UNION
-- get the oldest records
SELECT
r.ProductIdentifier,
IngredientIdentifier = min(r.IngredientIdentifier)
FROM
(
-- get most expensive record by cost
SELECT
r.ProductIdentifier,
r.IngredientIdentifier
FROM ActiveRecordsCTE a
CROSS APPLY
(
-- get most expensive record per product
SELECT
r.ProductIdentifier
,MaxAssetValue = MAX(r.Cost)
FROM ActiveRecordsCTE b
WHERE b.IsPrimary = a.IsPrimary
AND a.ProductIdentifier = b.ProductIdentifier
AND a.IngredientIdentifier = b.IngredientIdentifier
GROUP BY b.ProductIdentifier
) ca
WHERE a.IsPrimary = 0
-- exclude records that are included in the statement above
AND a.ProductIdentifier NOT IN
(
SELECT ProductIdentifier
FROM ActiveRecordsCTE
WHERE IsPrimary = 1
)
) sub
GROUP BY sub.ProductIdentifier
)
select * from [CalculatedPrimaryRecords]
Use row_number() for this type of prioritization:
with cte as ( . . . )
select t.*
from (select cte.*,
row_number() over (partition by product
order by (case when isActive = 1 and isPrimary = 1 then 1
when isActive = 0 and isPrimary = 1 then 2
else 3
end),
cost desc,
identifier asc
) as seqnum
from cte
) t
where seqnum = 1;
This makes some assumptions that seem consistent with the question:
isActive and isPrimary only take on the values 0 and 1.
If no records have isPrimary = 1, then you still want a record. (If not, these can easily be filtered out.)
identifier is not defined in your sample data.
EDIT:
If you wanted to be fancy, you could use top (1) with ties:
select top (1) with ties cte.*
from cte
order by row_number() over (partition by product
order by (case when isActive = 1 and isPrimary = 1 then 1
when isActive = 0 and isPrimary = 1 then 2
else 3
end),
cost desc,
identifier asc
);
I actually prefer the row_number() solution because I'm not sure what to do in the case that isPrimary = 0 and it is easier to add logic for that solution to filter out those records.

Update row based on value of multiple other rows in Oracle SQL

I want to find the rows which are similar to each other, and update a field if a row has any similar row. My table looks like this:
OrderID | Price | Minimum Number | Maximum Number | Volume | Similar
1 45 2 10 250 0
2 46 2 10 250 0
3 60 2 10 250 0
"Similar" in this context means that the rows that have same Maximum Number, Minimum Number, and Volume. Prices can be different, but the difference can be at most 2.
In this example, orders with OrderID of 1 and 2 are similar, but 3 has no similar row (since even if it has same Minimum Number, Maximum Number, and Volume, but its price is not within 2 units from orders 1 and 2).
Then, I want to update the filed "Similar" for orders 1 and 2 from the default value (0) to 1. So, the output for the example above would be:
OrderID | Price | Minimum Number | Maximum Number | Volume | Similar
1 45 2 10 250 1
2 46 2 10 250 1
3 60 2 10 250 0
Here is one method that is ANSI standard SQL that will work in most databases, including Oracle. It implements the logic that you set out using a correlated subquery:
update table t
set similar = 1
where exists (select 1
from table t2
where t2.minimum = t.minimum and
t2.maximum = t.maximum and
t2.volume = t.volume and
abs(t2.price - t.price) <= 2 and
t2.OrderId <> t.OrderId
);
EDIT:
It occurs to me that the "similar" field might be the minimum OrderId of the similar fields. You can extend the above idea to:
update table t
set similar = (select min(orderId)
from table t2
where t2.minimum = t.minimum and
t2.maximum = t.maximum and
t2.volume = t.volume and
abs(t2.price - t.price) <= 2 and
t2.OrderId <> t.OrderId
)
where exists (select 1
from table t2
where t2.minimum = t.minimum and
t2.maximum = t.maximum and
t2.volume = t.volume and
abs(t2.price - t.price) <= 2 and
t2.OrderId <> t.OrderId
);
Although if this were the case, the default value should be NULL and not 0.

Need a query to find count of a column record?

I have a table like this,
ProductId CategoryID bIsPrimary
1 5 1
1 6 0
1 7 0
2 18 1
2 19 1
I need a output like this,
ProductID PrimaryCategoryCount
1 1
2 2
Basically i need to find the the number of primary categories for each product.
SELECT ProductId, COUNT(*)
FROM SomeTable
WHERE bIsPrimary <> 0
GROUP BY ProductId
SELECT
ProductId
,sum(case when bIsPrimary = 1 then 1 else 0 end) as PrimaryCategoryCount
from
Table
group by
ProductId
or
SELECT
ProductId
,count(CategoryId)
from
Table
where bIsPrimiary = 1
group by ProductId
Both will provide you the same result. Pick up one which suits you more or is faster.
SELECT ProductId, COUNT(bIsPrimary)
FROM yourTable
GROUP BY ProductId
This is how I'd do it. WHERE clause isn't necessary here if I'm not mistaken.