Selecting table to use in FROM clause based on certain condition - sql

I have the following schema :
Purchase : pur_dt | pur_amt | item_code | quantity
ItemListForSoftware : item_code | item_desc | ...
ItemListForHardware : item_code | item_desc | ...
Now, I need to fetch item_desc from either of the Item tables based on the item_code of Purchase.
If item_code starts with S then it should look up in ItemListForSoftware
else from ItemListForHardware
Something like this
if(item_code starts with S){
select pur_dt,pur_amt,item_desc,quantity from Purchase, ItemListForSoftware where Purchase.item_code=ItemListForSoftware.item_code
}else{
select pur_dt,pur_amt,item_desc,quantity from Purchase, ItemListForHardware where Purchase.item_code=ItemListForHardware.item_code
}
Is there a way to do this via single SQL Query?

Is this what you want?
select p.pur_dt, p.pur_amt,
coalesce(ifs.item_desc, ifh.item_desc) as item_desc,
p.quantity
from Purchase p left join
ItemListForSoftware ifs
on p.item_code = ifs.item_code and
p.item_code like 'S%' left join
ItemListForHardware ifh
on p.item_code = ifh.item_code and
p.item_code not like 'S%';
Note the correct JOIN syntax.

You can solve this problem by LEFT JOINing to both tables and using the item_code to select the appropriate data:
SELECT p.pur_dt, p.pur_amt,
CASE WHEN LEFT(p.item_code, 1) = 'S' THEN s.item_desc
ELSE h.item_desc
END AS item_desc
p.quantity
FROM Purchase p
LEFT JOIN ItemListForSoftware s ON p.item_code=s.item_code
LEFT JOIN ItemListForHardware h ON p.item_code=h.item_code

Actually it is just as simple as you wrote:
select
pur_dt,
pur_amt,
case
when left(item_cide, 1) = 'S' then
(select item_desc from ItemListForSoftware as d where d.item_code = p.item_code)
else
(select item_desc from ItemListForHardware as d where d.item_code = p.item_code)
end as item_desc,
quantity
from Purchase as p;
Surely it will to work only if there is only 0 or 1 row in ItemListForSoftware and ItemListForHardware tables per item.
PS: Note that it could be faster a bit then joining three tables.

Related

SQL Joining tables with 'constants'

I have a table of orders,
Invoice Location Customer Code SalesPersonEmail
------------------------------------------------------
300001 001 CUS001 ?
300002 006 CUS002 ?
And a table of email groups,
Role Email
-----------------------------------------------------
Filtered_Group Management#gmail.com;john#gmail.com
When Location = 001, SalesPersonEmail must be the Email field from Filtered_Group
SalesPersonEmail for all other locations must be "Orders#gmail.com;" + the Email for Role No_Filter_Group.
I'm currently using the following to achieve this,
SELECT i.Invoice, i.Location, i.[Customer Code],
CASE WHEN i.Location = 001
THEN f.Email
ELSE N'Orders#gmail.com;' + nf.Email as SalesPersonEmail
END
FROM Invoice i, RoleCodes f, RoleCodes nf
WHERE f.Role = N'Filtered_Group' AND nf.Role = N'No_Filter_Group'
My problem is the Role No_Filter_Group may not exist in the Role table at times, which causes the above query to return nothing.
How do I join these tables properly so if No_Filter_Group does not exist in the table, rows that have a SalesPersonEmail of Filtered_Group are still returned from the query?
Thanks
A relatively simple way is to use LEFT JOIN and put the special number 001 for your location and special role names Filtered_Group and No_Filter_Group in the join condition.
In this SQL Fiddle you can comment/uncomment one line in the schema definition to see how it works when RoleCodes has a row with No_Filter_Group and when it doesn't.
In any case, the query would return all rows from Invoice table.
SELECT
Invoice.Invoice
,Invoice.Location
,Invoice.[Customer Code]
,CASE WHEN Invoice.Location = '001'
THEN RoleCodes.Email
ELSE 'Orders#gmail.com;' + ISNULL(RoleCodes.Email, '')
END AS SalesPersonEmail
FROM
Invoice
LEFT JOIN RoleCodes ON
(Invoice.Location = '001'
AND RoleCodes.Role = 'Filtered_Group')
OR
(Invoice.Location <> '001'
AND RoleCodes.Role = 'No_Filter_Group')
Try something like this.
Note: This is just a example am not sure about the tables and column of your schema. Replace with the respective tables and columns
SELECT CASE
WHEN location = '001' THEN (SELECT TOP 1 email
FROM email_table
WHERE [role] = 'Filtered_Group')
ELSE 'Orders#gmail.com;'
END
FROM orders
If email_table table will have only one row for [role] = 'Filtered_Group' then you can remove the TOP 1 from the sub-query
Left join or an easier, albeit less efficient method would be to do a subquery in the select statement itself.
SELECT i.Invoice, i.Location, i.[Customer Code],
CASE WHEN i.Location = 001
THEN (SELECT TOP 1 f.Email FROM RoleCodes f WHERE f.Role = N'Filtered_Group')
ELSE N'Orders#gmail.com;' + ISNULL( (SELECT nf.Email as SalesPersonEmail FROM RoleCodes nf WHERE nf.Role = N'No_Filter_Group'), '')
END
FROM Invoice i
Normally you would want to join these in on each other but I'm not certain how you would do that with the schema provided.
Nested select will be run for each row, instead, you could try this :-
Select i.Invoice
,i.Location
,i.CustomerCode
,Isnull(r.Email,'Orders#gmail.com') As SalesPersonEmail
From Invoice As i With (Nolock)
Left Join
(
Select rc.Email
,'001' As Location
From RoleCodes As rc With (Nolock)
Where rc.Role = 'Filtered_Group'
) As r On i.Location = r.Location
use the following Query:
select t.Invoice,t.Location,t.[Customer Code],
case t.Location
when '001' then
t2.Email
else
'Orders#gmail.com'
end
as
Salespersonemail
from orders t
join email_groups t2 on t2.Role='Filtered_Group'

SQL Server : outputting duplicates

I am using a prebuilt program so I am unable to directly edit the SQL, I can only edit snippets of it. The problem I am having is that the code is printing out the customer code for as many cases that customer has. For example, if a customer has 57 cases, it will print the customer code 57 times instead of just showing it once such as Customer Code 4 Cases 57 etc.
I was reading that you may not be able to use a Unique or Distinct function with the OVER command but I am not sure how else to make the sum work without it. Here is my code:
SET NOCOUNT ON;
DECLARE #ShowZeros nVarChar(4000);
SET #ShowZeros = 'N';
SELECT
SUM ([IC_ProductLots].[Available_Alt]) OVER(PARTITION BY [AR_Customers]. [CustomerCode]) AS [Cases]
, IC_Products.UnitOfMeasure_Alt
, SUM([IC_ProductLots].[Available_Stk]) OVER(PARTITION BY [AR_Customers]. [CustomerCode]) AS [Total Stock]
, IC_Products.UnitOfMeasure_Stk
, IC_Products.Description1
, IC_Products.ProductCode
, AR_Customers.Name
, AR_Customers.CustomerCode
FROM
((( DC_Transactions
INNER JOIN
AR_Customers ON DC_Transactions.CustomerKey = AR_Customers.CustomerKey)
INNER JOIN
IC_ProductLots ON DC_Transactions.LotKey = IC_ProductLots.LotKey)
INNER JOIN
IC_Products ON DC_Transactions.ProductKey = IC_Products.ProductKey)
WHERE
(IC_Products.ProductCode = ' 515070') AND
((CASE WHEN #ShowZeros = 'Y' or #ShowZeros = 'YES' THEN 1 ELSE
(ISNULL([IC_ProductLots].[Available_Stk],0))
END) > 0)
ORDER BY
IC_Products.ProductCode
, AR_Customers.CustomerCode
, AR_Customers.Name
Output should look similar to below:
Cases | U/M | Total Stock | Description | Cust Name | Cust Code
-----------------------------------------------------------------
57 | CS | 1779.45 | Food | Restaurant | 2
4 | CS | 120 | Dough | Bakery | 44
Currently it prints out 57 lines for customer code 2 and 4 lines for customer code 44, displaying the same information for each customer code.
The following seems to be more or less what you want. You say you can only partly edit your query, but well, you need the GROUP BY clause to say which records you want (i.e. one record per product and customer), so this is what mainly needs be done. Then you cannot use an OVER clause, as the aggregation group is given by GROUP BY already. I have made more changes for readability. See what edits you can do.
select
sum(l.available_alt) as cases
, p.unitofmeasure_alt
, sum(l.available_stk) as [total stock]
, p.unitofmeasure_stk
, p.description1
, p.productcode
, c.name
, c.customercode
from dc_transactions t
inner join ar_customers c on t.customerkey = c.customerkey
inner join ic_productlots l on t.lotkey = l.lotkey
inner join ic_products p on t.productkey = p.productkey
where p.productcode = ' 515070'
and (#showzeros in ('Y', 'YES') or l.available_stk > 0)
group by p.productcode, c.customercode
, p.unitofmeasure_alt, p.unitofmeasure_stk, p.description1, c.name
order by p.productcode, c.customercode;

how to join two tables where second table has condtion

I have two tables like
shopping_cart_table and customer_table
shopping_cart_table has fields shoppingcart_id | home_No|
customer_table has fields shoppingcart_id | status| customer_type|
I want to get the home_No from shopping_cart_table WHERE (customer_table.status is null OR customer_table.status='Y') AND customer_table.customer_type='X'
both table can join from shoppingcart_id
Actually this is just basic.
You can use join & put where conditions.
Select a.home_No
from shopping_cart_table as a
inner join customer_table as b
on a.shoppingcart_id = b.shoppingcart_id
where nvl(b.status,'Y') = 'Y'
and customer_type='X'
select home_No
from shopping_cart_table, customer_table
WHERE shopping_cart_table.shoppingcart_id = customer_table.shoppingcart_id
AND(customer_table.status is not null OR customer_table.status='Y') AND
customer_table.customer_type='X'
But this condition looks a bit strange:
(customer_table.status is not null OR customer_table.status='Y')
Maybe you'd want to change it for
nvl(customer_table.status,'Y')='Y'
aqssuming that 'not' was put there by a mistake
You can try this query:
SELECT sct.home_no
FROM shopping_cart_table AS sct
, customer_table AS ct
WHERE sct.shoppingcart_id = ct.shoppingcart_id
AND (
ct.status IS NOT NULL
OR ct.status = 'Y')
AND ct.customer_type = 'X'

Multiple Join on the same table-Oracle

I tried the below query
SELECT Customer.*,
ElectrnicItem.Product1 AS ElectronicItem1,
ElectrnicItem.Product2 AS ElectronicItem2,
ElectrnicItem.Product3 AS ElectronicItem3,
ApparelItem.Product1 AS ApparelItem1,
ApparelItem.Product2 AS ApparelItem2,
ApparelItem.Product3 AS ApparelItem3
FROM Customer
LEFT JOIN Inventory AS ElectrnicItem
ON (Customer.CustomerID = ElectrnicItem.CustomerID)
LEFT JOIN Inventory AS ApparelItem
ON (Customer.CustomerID = ApparelItem.CustomerID)
but it always returns:
ORA-00918 column ambiguously defined
You're missing any predicate to identify rows in Inventory as either 'Electronic' or 'Apparel' for one thing. Not sure that's the fix though.
How about this?
SELECT a.customer_id, a.customer_name, b.product1 electronicitem1,
b.product2 electronicitem2, b.product3 electronicitem3,
c.product1 apparelitem1, c.product2 apparelitem2,
c.product3 apparelitem3
FROM customer a, inventory b, inventory c
WHERE a.customer_id = b.customer_id(+)
AND b.product_type(+) = 'Electronic'
AND a.customer_id = c.customer_id(+)
AND c.product_type(+) = 'Apparel';
I created tables Customer and Inventory, also inserted the values that you have provided. The following is the query :
select customer.*,ElectrnicItem.Product1 as ElectronicItem1,
ElectrnicItem.Product2 as ElectronicItem2,
ElectrnicItem.Product3 as ElectronicItem3,
ApparelItem.Product1 as ApparelItem1,
ApparelItem.Product2 as ApparelItem2,
ApparelItem.Product3 as ApparelItem3
from customer
left join inventory as ElectrnicItem on
(customer.CustomerID = ElectrnicItem.CustomerID )
left join inventory as ApparelItem
on (customer.CustomerID = ApparelItem.CustomerID )
It works fine and gives the following result:
1 David Miller mobile headphone trimmer mobile headphone trimmer
2 Johnson jeans tshirt NULL jeans tshirt NULL
3 Diggs NULL NULL NULL NULL NULL NULL
The expected output that you have mentioned will never be achieved. Because Customer table contains CustomerId and CustomerName, Customer.* will retrieve both CustomerId and CustomerName. So CustomerName column can not contain any NULL
values.
Ok, a bit late, but...
You donĀ“t use AS to alias a table in Oracle, only to alias a column. Try to drop all those AS from the tables aliases:
SELECT Customer.*,
ElectrnicItem.Product1 AS ElectronicItem1,
ElectrnicItem.Product2 AS ElectronicItem2,
ElectrnicItem.Product3 AS ElectronicItem3,
ApparelItem.Product1 AS ApparelItem1,
ApparelItem.Product2 AS ApparelItem2,
ApparelItem.Product3 AS ApparelItem3
FROM Customer
LEFT JOIN Inventory ElectrnicItem
ON (Customer.CustomerID = ElectrnicItem.CustomerID)
LEFT JOIN Inventory ApparelItem
ON (Customer.CustomerID = ApparelItem.CustomerID)

Trying to Calculate Qty on Hand and Value on hand with multiple shipments

I have this query, I have an Acquisitions table (Incoming) and Invoice Table (Outgoing) I am trying to calculate the Value on Hand by taking the AVG dbo.tblAcqDetail.AcqPrice * the QtyOnHand which is figured taking Incoming - Outgoing. When I add a line item on the Acquisitions table that has a different cost for the same item, the AVG is not grouping and showing instead two line items example below. The shipment side works fine with multiple line items...
Product QtyIn QtyOut On_Hand AVGPrice Value_OnHand
Screws 100 30 70 25.0000 1750.0000
Nuts 50 10 40 40.0000 1600.0000
Nuts 100 10 90 50.0000 4500.0000
Bolts 100 20 80 100.000 8000.0000
.
SELECT DISTINCT
dbo.tblProduct.Product ,
SUM(DISTINCT dbo.tblAcqDetail.AcqQuantity) AS QtyIN ,
SUM(DISTINCT dbo.tblInvoiceDetail.InvQuantity) AS QtyOut ,
SUM(DISTINCT dbo.tblAcqDetail.AcqQuantity)
- SUM(DISTINCT dbo.tblInvoiceDetail.InvQuantity) AS On_Hand ,
dbo.tblAcqDetail.AcqPrice ,
dbo.tblAcqDetail.AcqPrice
* ( SUM(DISTINCT dbo.tblAcqDetail.AcqQuantity)
- SUM(DISTINCT dbo.tblInvoiceDetail.InvQuantity) ) AS Value_Hand
FROM dbo.tblAcq
INNER JOIN dbo.tblAcqDetail ON dbo.tblAcq.acqID = dbo.tblAcqDetail.AcqID
INNER JOIN dbo.tblProduct ON dbo.tblAcqDetail.ProductID = dbo.tblProduct.ProductID
INNER JOIN dbo.tblInvoiceDetail ON dbo.tblProduct.ProductID = dbo.tblInvoiceDetail.ProductID
INNER JOIN dbo.tblInvoice ON dbo.tblInvoiceDetail.InvoiceID = dbo.tblInvoice.InvoiceID
GROUP BY dbo.tblProduct.Product ,
dbo.tblAcqDetail.AcqPrice
Basing on PinnyM's answer, you don't need DISTINCT, I re-write your query as following using table alias:
SELECT
P.Product ,
SUM( AcD.AcqQuantity) AS QtyIN ,
SUM( InD.InvQuantity) AS QtyOut ,
SUM( AcD.AcqQuantity)
- SUM( InD.InvQuantity) AS On_Hand ,
AcD.AcqPrice ,
AcD.AcqPrice
* ( SUM(AcD.AcqQuantity)
- SUM( InD.InvQuantity) ) AS Value_Hand
FROM dbo.tblAcq Ac
INNER JOIN dbo.tblAcqDetail AcD ON Ac.acqID = AcD.AcqID
INNER JOIN dbo.tblProduct P ON AcD.ProductID = P.ProductID
INNER JOIN dbo.tblInvoiceDetail InD ON P.ProductID = InD.ProductID
INNER JOIN dbo.tblInvoice Inv ON InD.InvoiceID = Inv.InvoiceID
GROUP BY P.Product ,
AcD.AcqPrice
By reading this query, I don't understand why you need table dbo.tblInvoice, it is not part of aggregation.
The reason you still see different product is because you group by two columns P.Product, AcD.AcqPrice, not only Product, in your return result you can see their combination is unique.
To be mathematically accurate, you should not be using SUM(DISTINCT fieldname), but just SUM(fieldname). Otherwise, it will eliminate entries that happen to have the same quantity.
For that matter, you shouldn't be using DISTINCT at the beginning of your query either, GROUP BY already handles that.
If you believe you have duplicate rows being returned by your JOINs (which you shouldn't really if you're doing it right), wrap them in a subquery using DISTINCT before trying to aggregate.
As an example, a subquery to eliminate duplicates can be written like so:
SELECT
Product ,
SUM(AcqQuantity) AS QtyIN ,
SUM(InvQuantity) AS QtyOut ,
SUM(AcqQuantity)
- SUM(InvQuantity) AS On_Hand ,
AcqPrice ,
AcqPrice
* ( SUM(AcqQuantity)
- SUM(InvQuantity) ) AS Value_Hand
FROM (SELECT DISTINCT
dbo.tblProduct.Product ,
dbo.tblAcqDetail.AcqQuantity,
dbo.tblInvoiceDetail.InvQuantity,
dbo.tblAcqDetail.AcqPrice
FROM
dbo.tblAcqDetail
INNER JOIN dbo.tblProduct ON dbo.tblAcqDetail.ProductID = dbo.tblProduct.ProductID
INNER JOIN dbo.tblInvoiceDetail ON dbo.tblProduct.ProductID = dbo.tblInvoiceDetail.ProductID
INNER JOIN dbo.tblInvoice ON dbo.tblInvoiceDetail.InvoiceID = dbo.tblInvoice.InvoiceID ) productInfo
GROUP BY Product, AcqPrice