Concatenating a column within the same column in SQL Server - sql

Hi so I have a question on concatenating a column. I have two tables.
Table 1: Sales Order-> OrderID, ProductsOnHold (Should list product(s) on hold)
Table 2: Products-> ProductID, OnHold (boolean)
A Sales Order can have many products.
A product can have a hold, which would make the Sales Order be put on hold if that order has that product.
So a sale order can be on hold for have 1 or more products on hold.
I was able to get one product to display if the order is on hold,
but what if it has more products on hold -
how could I display them as well?
This is for a view I am creating.
This is what I have done so far:
(SELECT ProductName
FROM Products with (NOLOCK)
WHERE (OnHold = 1) AND (EXISTS
(SELECT CASE
WHEN (COUNT(DISTINCT Product)> 1) THEN (Product + ', ' + Product)
ELSE Product END AS ProductName
FROM SalesOrder WITH (NOLOCK)
GROUP BY OrderID ))) AS ProductsOnHold
Desired Output:
OrderID | ProductsOnHold
----------------------------------
1 | P1, P2, P7
2 |
3 | P1
4 | P1, P7, P8, P9, P15, P77
Anything I am missing in my sql query?

Assuming that you have 2 tables SalesOrder and Product with ProductID as the "relationship" column, you can join those 2 tables and then transpose using a "for xml" clause.
select s1.OrderID,
stuff(
(select ','+p.ProductName as [text()]
from SalesOrder s
join Product p on p.ProductID = s.ProductID and p.OnHold = true
where s.OrderID = s1.OrderID
group by p.ProductName
for xml path (''))
,1,1,'') as ProductsOnHold from SalesOrder s1 group by s1.OrderID
Hope this helps.

My solution [I am not sure if I understand your problem well]
user function
create function inonerow (#orderid int)
returns nvarchar(max)
begin
declare #data nvarchar(max)
SELECT #data =coalesce(#data + ',','')+ CONVERT(varchar,prodid) from SalesOrder
inner join product on product.prodid = salesorder.prodid where orderid=#orderid and product.oshold = 1
return #data
end
select distinct orderid, dbo.inonerow(orderid) from salesorder
inner join product on product.prodid = salesorder.prodid
where product.onhold = 1
Hope this works :)

Related

Oracle SQL - Sum up and show text if > 0

I'm totally new with Oracle SQL and my question may seem stupid but I have some difficulties to solve my problem.
Current situation:
I have following tables: Supplier, Debtor, Invoice
Every Supplier has various Debtors and every Debtor has various Invoices.
I want to create an evaluation which shows me the following scenario:
A list of all Debtors from a Supplier. I also want to see in that list if the Debtor once haven't payed his invoice. I have this information within the table Invoice as the attribute called "payed" and possible values are 0 (payed) and 1 (not payed). I just want to see the debtor ONCE in the list so if there is only ONE invoice which is 1, it should show me "1" or "Not Payed" in the list. Right now when a debtor has 100 invoices it shows me 100 times the debtor with the info "0" or "1".
Currently:
SELECT company.company_id,
company.companyname_1,
supplier.supplier_id,
supplier.suppliername_1,
debtor.debtor_id_from_supplier,
debtor_ext.debtorname_1,
debtor_ext.street,
debtor_ext.street_number,
debtor_ext.postcode,
debtor_ext.city,
debtor.approved_limit,
debitor.limit_left,
debitor.limit_status,
debitor.limit_type,
debitor.prosecution,
CASE
WHEN invoice.payed = 0 THEN 'Yes'
ELSE 'No'
END as deb_payment
FROM debtor,
debtor_ext,
company,
supplier,
invoice
WHERE ( company.company_id = supplier.company_id ) and
( supplier.supplier_id = debtor.supplier_id ) and
( debtor.debtor_id = debtor_ext.debtor_id ) and
( debtor.supplier_id = invoice.supplier_id ) and
( debtor.debtor_id_from_supplier = invoice.debtor_id_from_supplier )
CODE CORRECTED!
Hope you guys can help me
First of all, In your query, you're doing cross join which is not required and also affects on the performance. I'll advice you to use Inner Join here.
Also,as the schema is not provided in question,I am giving solution for the below schema:
Supplier:
Supplier_Id (PK) | Supplier_Name
Debtor:
Debtor_Id (PK) | Debtor_Name | Supplier_Id (FK)
Invoice:
Invoice_Id (PK) | Supplier_Id (FK) | Debtor_Id (FK) | Amount | Payed
Query:
Select company.company_id,
company.companyname_1,
s.supplier_id,
s.suppliername_1,
d.debtor_id_from_supplier,
d.debtorname_1,
d.street,
d.street_number,
d.postcode,
d.city,
d.approved_limit,
d.limit_left,
d.limit_status,
d.limit_type,
d.prosecution,
tmp.deb_payment
From
(
SELECT d.supplier_id,
d.debtor_id,
(
CASE
WHEN min(i.payed) <> max(i.payed) Then 'Not Payed'
ELSE 'Payed'
END
)as deb_payment
FROM supplier s
inner join debtor d
on s.supplier_id = d.supplier_id
inner join invoice i
on i.supplier_id = d.supplier_id
and i.debtor_id = d.debtor_id
Group by d.supplier_id,d.debtor_id
) tmp
inner join supplier s
on s.supplier_id = tmp.supplier_id
inner join company
on company.company_id = s.company_id
inner join debtor d
on s.supplier_id = d.supplier_id
inner join debtor_ext de
d.debtor_id = de.debtor_id
;
Hope it helps!

Updating Product Order is not working

I have Products table with (Name, ParentID, Order) columns. I have a insert statement which keep the inserted child products. After the insert I need to update the order,
I have the following SQL,
UPDATE Products
SET [Order] = (SELECT ISNULL(MAX([Order]), 0) + 1 FROM Products WHERE ParentID = CP.ParentID)
FROM Products P
INNER JOIN #InsertedChildProduct CP ON (CP.ID = P.ID)
The problem is that I am updating the order of products that are just inserted, but [Order] is not working. If I have,
Products
--------
ParentID Order
----------------
1 1
1 2
and let say I have inserted 2 child products then the table should be,
Products
--------
ParentID Order
----------------
1 1
1 2
1 3
1 4
But I am seeing,
Products
--------
ParentID Order
----------------
1 1
1 2
1 3
1 3
You can try this, taken from the answers on here:
declare #MaxNumber int
set #MaxNumber = 0
UPDATE Products
SET [Order] = #MaxNumber, #MaxNumber = (SELECT ISNULL(MAX([Order]), 0)
FROM Products
WHERE ParentID = CP.ParentID) + 1
FROM Products P
INNER JOIN #InsertedChildProduct CP ON (CP.ID = P.ID)
Try like this instead, you need to gt the MAX() first in inner query
UPDATE P
SET [Order] = X.newval
FROM Products P
JOIN
(
SELECT ID, (ISNULL(MAX([Order]), 0) + 1) as newval
FROM Products P
JOIN #InsertedChildProduct ip
on P.ParentID = ip.ParentID
group by ID
) X
ON X.ID = P.ID
Thought about this a bunch of different ways and can't see how this would work without imposing order on both datasets, even if arbitrary. Here's one way to do it (Fiddle - make sure to build the schema first, then run the code): http://www.sqlfiddle.com/#!3/d34df/3)
WITH cteRN_c
AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID) AS RN_c,
ID
FROM #InsertedChildProduct
),
cteRN_p
AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY ParentID ORDER BY [Order]) AS RN_p,
ParentID,
[Order]
FROM Products
WHERE [Order] IS NULL
)
UPDATE p
SET [Order] = (SELECT ISNULL(MAX([ORDER]), 0) FROM Products WHERE ParentID = p.ParentID) + c.RN_c
FROM cteRN_p p INNER JOIN cteRN_c c
ON p.ParentID = c.ID AND
p.RN_p = c.RN_c;
We impose order by adding arbitrary row numbers to both the temp table set and the parent set, via ROW_NUMBER in CTEs. From that point, it's just a matter of joining the CTEs on the correct datapoints, and running the updates against the parent CTE. Granted, it's arbitrary which child will get numbered in which order, but at least it will happen.
Edit: Forgot the ISNULL in the MAX portion of the query - in case no children yet. Fiddle updated as well.

Combine multiple row values into one row

I have a result set that should be pulled into a report as one line. However, there can be multiple buyers associated to an order and these buyers are represented as a new row in the database. So for instance, I have the following...
SELECT
O.OrdersID
,BS.LastName
FROM
Orders O
LEFT JOIN
BuyerSeller BS ON O.OrdersID = BS.OrdersID
If there are multiple buyers, it will return the following result set as follows:
OrdersID | LastName
----------------------
1 | Tester1
1 | Tester2
1 | Tester3
I'd like it to return as the following (separated by columns):
OrdersID | LastName
---------------------------------------
1 | Tester1, Tester2, Tester3
Thanks for the assistance.
Here is your Answer.
SELECT DISTINCT Ord.OrderID , substring
((SELECT ',' + BS.LastName AS [text()]
FROM Orders O LEFT JOIN BuyerSeller BS ON O.OrderID = BS.OrderID
ORDER BY O.OrderID FOR XML PATH('')), 2, 1000) LastName
FROM Orders ord
This will return the expected output.
To accomplish this in SSRS you would need to
Create a table with OrdersID as Row group
Make sure there is no detail section inside the group. If there is one delete it without deleting the rows.
Write this experession for LastName:
=Join(Lookupset(Fields!OrdersID.Value, Fields!OrdersID.Value, Fields!LastName.Value, "myDataSet"), ", ")
Remember SSRS is case sensitive.

Query Returning SUM of Quantity or 0 if no Quantity for a Product at a given date

I have the following 4 tables:
----------
tblDates:
DateID
30/04/2012
01/05/2012
02/05/2012
03/05/2012
----------
tblGroups:
GroupID
Home
Table
----------
tblProducts:
ProductID GroupID
Chair Home
Fork Table
Knife Table
Sofa Home
----------
tblInventory:
DateID ProductID Quantity
01/05/2012 Chair 2
01/05/2012 Sofa 1
01/05/2012 Fork 10
01/05/2012 Knife 10
02/05/2012 Sofa 1
02/05/2012 Chair 3
03/05/2012 Sofa 2
03/05/2012 Chair 3
I am trying to write a query that returns all Dates in tblDates, all GroupIDs in tblGroups and the total Quantity of items in each GroupID.
I manage to do this but only get Sum(Quantity) for GroupID and DateID that are not null. I would like to get 0 instead. For exemple for the data above, I would like to get a line "02/05/2012 Table 0" as there is no data for any product in "Table" group on 01/05/12.
The SQL query I have so far is:
SELECT tblDates.DateID,
tblProducts.GroupID,
Sum(tblInventory.Quantity) AS SumOfQuantity
FROM (tblGroup
INNER JOIN tblProducts ON tblGroup.GroupID = tblProducts.GroupID)
INNER JOIN (tblDates INNER JOIN tblInventory ON tblDates.DateID = tblInventory.DateID) ON tblProducts.ProductID = tblInventory.ProductID
GROUP BY tblDates.DateID, tblProducts.GroupID;
I reckon I should basically have the same Query on a table different from tblInventory that would list all Products instead of listed products with 0 instead of no line but I am hesitant to do so as given the number of Dates and Products in my database, the query might be too slow.
There is probably a better way to achieve this.
Thanks
To get all possible Groups and Dates combinations, you'll have to CROSS JOIN those tables. Then you can LEFT JOIN to the other 2 tables:
SELECT
g.GroupID
, d.DateID
, COALESCE(SUM(i.Quantity), 0) AS Quantity
FROM
tblDates AS d
CROSS JOIN
tblGroups AS g
LEFT JOIN
tblProducts AS p
JOIN
tblInventory AS i
ON i.ProductID = p.ProductID
ON p.GroupID = g.GroupID
AND i.DateID = d.DateID
GROUP BY
g.GroupID
, d.DateID
use the isnull() function. change your query to:
SELECT tblDates.DateID, tblProducts.GroupID,
**ISNULL( Sum(tblInventory.Quantity),0)** AS SumOfQuantity
FROM (tblGroup INNER JOIN tblProducts ON tblGroup.GroupID = tblProducts.GroupID)
INNER JOIN (tblDates INNER JOIN tblInventory ON tblDates.DateID = tblInventory.DateID)
ON tblProducts.ProductID = tblInventory.ProductID
GROUP BY tblDates.DateID, tblProducts.GroupID;
You don't say which database you're using but what I'm familiar with is Oracle.
Obviously, if you inner join all tbales, you will not get the rows containing nulls. If you use an outer join to the inventory table you also have a problem because the groupid column is not listed in tblinventory and you can only outer join to one table.
I'd say you have two choices. Use a function or have a duplicate of the groupid column in the inventory table.
So, a nicer but slower solution:
create or replace
function totalquantity( d date, g varchar2 ) return number as
result number;
begin
select nvl(sum(quantity), 0 )
into result
from tblinventory i, tblproducts p
where i.productid = p.productid
and i.dateid = d
and p.groupid = g;
return result;
end;
select dateid, groupid, totalquantity( dateid, groupid )
from tbldates, tblgroups
Or, if you include the groupid column in the inventory table. (You can still quarantee integrity using constraints)
select d.dateid, i.groupid, nvl(i.quantity, 0)
from tbldates d, tblinventory i
where i.dateid(+) = d.dateid
group by d.dateid, g.groupid
Hope this helps,
GĂ­sli

How to ensure outer join with filter still returns all desired rows?

Imagine I have two tables in a DB like so:
products:
product_id name
----------------
1 Hat
2 Gloves
3 Shoes
sales:
product_id store_id sales
----------------------------
1 1 20
2 2 10
Now I want to do a query to list ALL products, and their sales, for store_id = 1. My first crack at it would be to use a left join, and filter to the store_id I want, or a null store_id, in case the product didn't get any sales at store_id = 1, since I want all the products listed:
SELECT name, coalesce(sales, 0)
FROM products p
LEFT JOIN sales s ON p.product_id = s.product_id
WHERE store_id = 1 or store_id is null;
Of course, this doesn't work as intended, instead I get:
name sales
---------------
Hat 20
Shoes 0
No Gloves! This is because Gloves did get sales, just not at store_id = 1, so the WHERE clause has filtered them out.
How then can I get a list of ALL products and their sales for a specific store?
Here are some queries to create the test tables:
create temp table test_products as
select 1 as product_id, 'Hat' as name;
insert into test_products values (2, 'Gloves');
insert into test_products values (3, 'Shoes');
create temp table test_sales as
select 1 as product_id, 1 as store_id, 20 as sales;
insert into test_sales values (2, 2, 10);
UPDATE: I should note that I am aware of this solution:
SELECT name, case when store_id = 1 then sales else 0 end as sales
FROM test_products p
LEFT JOIN test_sales s ON p.product_id = s.product_id;
however, it is not ideal... in reality I need to create this query for a BI tool in such a way that the tool can simply add a where clause to the query and get the desired results. Inserting the required store_id into the correct place in this query is not supported by this tool. So I'm looking for other options, if there are any.
Add the WHERE condition to the LEFT JOIN clause to prevent that rows go missing.
SELECT p.name, coalesce(s.sales, 0)
FROM products p
LEFT JOIN sales s ON p.product_id = s.product_id
AND s.store_id = 1;
Edit for additional request:
I assume you can manipulate the SELECT items? Then this should do the job:
SELECT p.name
,CASE WHEN s.store_id = 1 THEN coalesce(s.sales, 0) ELSE NULL END AS sales
FROM products p
LEFT JOIN sales s USING (product_id)
Also simplified the join syntax in this case.
I'm not near SQL, but give this a shot:
SELECT name, coalesce(sales, 0)
FROM products p
LEFT JOIN sales s ON p.product_id = s.product_id AND store_id = 1
You don't want a where on the whole query, just on your join