I tried researching before asking, but couldn't find anything or maybe I used the wrong keywords. I might get a -1 for that, but anyway.
In the exercise I'm doing I need to select the following expression
salary + comission value + trip expenses - discounts.
This is my select:
select a.id_vendor, a.name_vendor, a.id_region, a.salary + b.value + c.value - d.value
from vendors a
inner join comission b
on a.id_vendor = b.id_vendor
inner join trip_expenses c
on a.id_vendor = c.id_vendor
inner join discounts d
on a.id_vendor = d.id_vendor
group by a.id_vendor, a.name_vendor, a.id_region, a.salary, b.value, c.value, d.value;
I've tried adding sum to some of the number records, but didn't work.
The thing is, that when I execute this select it brings more than one record for the same Vendor ID, and I would like that they be summed into just one column.
This is the result:
id_vendor name_vendor id_region
----------- ------------------------------ ----------- ---------------------------------------
1 Marco 1 1955.00
1 Marco 1 2180.00
2 Walesca 2 3190.00
3 Carol 3 4970.00
4 Evelyn 4 1860.00
5 Wallace 5 10130.00
As you can see, Marco comes twice because he has more than one record on the discounts table.
How do correct that? What's wrong with my select?
Thanks in advance!
Without seeing the structure of your discounts table, it is hard to provide a definitive answer.
That said, and assuming that you have date ranges or some other indicator of when the discount should be applied, add that to your query in a where clause.
select a.id_vendor, a.name_vendor, a.id_region, a.salary + b.value + c.value - d.value
from vendors a
inner join comission b
on a.id_vendor = b.id_vendor
inner join trip_expenses c
on a.id_vendor = c.id_vendor
inner join discounts d
on a.id_vendor = d.id_vendor
-- Add a where clause looking at Start/End Dates
where d.StartDate < GetDate() and getdate() < d.EndDate
group by a.id_vendor, a.name_vendor, a.id_region, a.salary, b.value, c.value, d.value;
Hopefully this helps. If not, you may have to post your schema for your tables so we have a better idea of what you are working with.
Related
I have a problem with the optimization of this query, I have 3 tables (Products = Catalogo.GTIN, Sales Header = TEDEF.Factura and Sales Detail = TEDEF.Farmacia).
The query tries to find the Mode of the column VPRODEXENIGV_FAR. This query without the ORDER BY executes in less than 3 seconds (the table of details has about 30 million rows).
But when I add the ORDER BY clause, the query now takes more than 30 minutes to run.
I want to know how can I optimize this query or the indexes that I need to optimize this.
SELECT *
FROM Catalogo.GTIN G
CROSS APPLY
(SELECT TOP 1
COUNT(FAR.VPRODEXENIGV_FAR) [ROW],
YEAR(FAC2.VFECEMI_FAC) [AÑO],
MONTH(FAC2.VFECEMI_FAC) [MES],
FAR.VCODPROD_FAR_003,
CASE WHEN FAR.VPRODEXENIGV_FAR = 'A' THEN 1 ELSE 0 END AfectoIGV
FROM
TEDEF.Factura FAC2
INNER JOIN
TEDEF.Farmacia FAR ON FAC2.VTDOCPAGO_FAC = FAR.VTDOCPAGO_FAC
AND FAC2.VNDOCPAGO_FAC = FAR.VNDOCPAGO_FAC
WHERE
G.CODIGO = FAR.VCODPROD_FAR_003
GROUP BY
YEAR(FAC2.VFECEMI_FAC),
MONTH(FAC2.VFECEMI_FAC),
FAR.VCODPROD_FAR_003,
FAR.VPRODEXENIGV_FAR
ORDER BY
1 DESC --- <----- THE PROBLEM IS HERE
) GG
Ouch! You have a hugely expensive dependent subquery. It's expensive because SELECT TOP(n) ... ORDER BY col DESC does a whole lot of work to create a result set only to discard all but one row. And, it's a dependent subquery so it's run for every row of Catalogo.GTIN .
It looks like you want to count the resultset rows in the most recent month and year for each Catalogo.GTIN row. So, let's try to refactor your query to do that.
We'll start with a subquery to grab the month-start date of the latest Factura row for each catalog entry.
SELECT CODIGO,
DATEFROMPARTS(YEAR(maxd), MONTH(maxd),1) maxmes
FROM (
SELECT MAX(FAC2.VFECEMI_FAC) maxd,
G.CODIGO
FROM Catalogo.GTIN G
JOIN TDEF.Farmacia FAR
ON G.CODIGO = FAR.VCODPROD_FAR_003
JOIN TEDEF.Factura FAC2
ON FAC2.VTDOCPAGO_FAC = FAR.VTDOCPAGO_FAC
AND FAC2.VNDOCPAGO_FAC = FAR.VNDOCPAGO_FAC
GROUP BY G.CODIGO
) maxd
It's wise to test this and make sure it works correctly and performs tolerably well. If you test it in SSMS, you can use "Show Actual Execution Plan" and see if it recommends an extra index. This subquery need only be run once, rather than once per G.CODIGO row.
Then we'll use it in your larger query.
SELECT G.*,
COUNT(FAR.VPRODEXENIGV_FAR) [ROW],
YEAR(FAC2.VFECEMI_FAC) [AÑO],
MONTH(FAC2.VFECEMI_FAC) [MES],
FAR.VCODPROD_FAR_003,
CASE WHEN FAR.VPRODEXENIGV_FAR = 'A' THEN 1 ELSE 0 END AfectoIGV
FROM Catalogo.GTIN G
JOIN (
SELECT CODIGO,
DATEFROMPARTS(YEAR(maxd), MONTH(maxd),1) maxmes
FROM (
SELECT MAX(FAC2.VFECEMI_FAC) maxd,
G.CODIGO
FROM Catalogo.GTIN G
JOIN TDEF.Farmacia FAR
ON G.CODIGO = FAR.VCODPROD_FAR_003
JOIN TEDEF.Factura FAC2
ON FAC2.VTDOCPAGO_FAC = FAR.VTDOCPAGO_FAC
AND FAC2.VNDOCPAGO_FAC = FAR.VNDOCPAGO_FAC
GROUP BY G.CODIGO
) maxd
) maxmes ON G.CODIGO = maxmes.CODIGO
JOIN TEDEF.Farmacia FAR
ON G.CODIGO = FAR.VCODPROD_FAR_003
JOIN TEDEF.Factura FAC2
ON FAC2.VTDOCPAGO_FAC = FAR.VTDOCPAGO_FAC
AND FAC2.VNDOCPAGO_FAC = FAR.VNDOCPAGO_FAC
AND FAC2.VFECEMI_FAC >= maxmes.maxmes
GROUP BY maxmes.maxmes,
G.CODIGO,
FAR.VCODPROD_FAR_003,
FAR.VPRODEXENIGV_FAR
Here is the tricky bit:
DATEFROMPARTS(YEAR(maxd), MONTH(maxd),1) maxmes turns any date maxd into the first day of that month.
And, FAC2.VFECEMI_FAC >= maxmes.maxmes filters out rows before the first day of that month (for that CODIGO). It does so in a sargable way: a way that can exploit an index on FAC2.VFECEMI_FAC.
That is an alternative way to do TOP(1) ORDER BY d DESC. And faster.
It's all about sets of rows. Especially when using GROUP BY, it's performance-helpful to limit the number of rows in each set.
Obviously I cannot debug this.
Is me again, Finally i resolve the problem of the optimization, now the query delay is about 20 sec (with the sort instruction and with the count in a table over 30 million rows) i hope this way can help others or could be optimice more by the community.
I resolve the problem applying the sort but with the Row_Number instruction, in that way the server take my index for the sort instruction and make the magic:
WITH x
AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY GG.COD, GG.[AÑO], GG.[MES] ORDER BY GG.[ROW] DESC) [ID]
FROM Catalogo.GTIN G
CROSS APPLY
(
SELECT COUNT(FAR.VPRODEXENIGV_FAR) [ROW]
, YEAR(FAC2.VFECEMI_FAC) [AÑO]
, MONTH(FAC2.VFECEMI_FAC) [MES]
, FAR.VCODPROD_FAR_003 [COD]
, CASE WHEN FAR.VPRODEXENIGV_FAR = 'A' THEN 1 ELSE 0 END AfectoIGV
FROM TEDEF.Factura FAC2
INNER JOIN TEDEF.Farmacia FAR
ON FAC2.VTDOCPAGO_FAC = FAR.VTDOCPAGO_FAC
AND FAC2.VNDOCPAGO_FAC = FAR.VNDOCPAGO_FAC
WHERE G.CODIGO = FAR.VCODPROD_FAR_003
GROUP BY YEAR(FAC2.VFECEMI_FAC)
, MONTH(FAC2.VFECEMI_FAC)
, FAR.VCODPROD_FAR_003
, FAR.VPRODEXENIGV_FAR
-- ORDER BY 1 DESC --- <---- this is the bad guy, please, don't do that xD
) GG
) SELECT *
FROM x WHERE ID = 1
In that way i can sort the Count instruction and calculate the Mode for the Column FAR.VPRODEXENIGV_FAR
I think my attempt of wording this is going to be more difficult than sharing the code; However...
Firstly, I'm using SQL Server and extracting information from SAP B1.
I'm trying to generate a CSV file from my SQL Dataset to import into a logistics solution.
OrderNo
NumberOfItems
ItemNumber
12345
2
1
12345
2
2
45678
3
1
45678
3
2
45678
3
3
Using the above table, Order number 12345 would be a TOP 2 function, Order number 45678 would be a TOP 3 function.
The #TOPNUMBER will be pulled from an invoice within SAP so it will be dynamic for each Order number.
When I've writte my query I have performed an inner join using <> to bring through thousands of individual results, then filtering down using the TOP function. (which may have been my first mistake)
DECLARE #NUM AS INT = 3
SELECT TOP (#NUM) --Top Function to be replaced with "Number Of ITems"
T0.[DocNum] AS 'Order_No',
ISNULL(T0.NumAtCard,'') AS 'Cust_Ord_No',
Convert(varchar,T0.DocDate,103) AS 'Order_Date',
'Delivery' AS 'Order_Type',
T0.CardName as 'Del_Name',
ISNULL(T12.buIldingS,'') AS 'Del_Addr_1',
ISNULL(T12.BlockS,'') AS 'Del_Addr_2',
ISNULL(T12.StreetS,'') AS 'Del_Addr_3',
ISNULL(T12.CityS,'') AS 'Del_Town',
ISNULL(T12.CountyS,'') AS 'Del_County',
ISNULL(T12.ZipCodeS,'') AS 'Postal_code',
ISNULL(T12.CountryS,'') AS 'Ctry_Code',
ISNULL(t5.Tel1,'') AS 'Contact_Tel_1',
ISNULL(T5.Tel2,'') AS 'Contact_Tel_2',
ISNULL(T5.Cellolar,'') AS 'Contat_Tel_Mob',
ISNULL(T5.E_mailL,'') AS 'Contact_Email',
ISNULL(T0.Comments,'') AS 'Order_Notes',
'00:05' AS 'Unload_Time',
'' AS 'Delivery_Method',
convert(varchar,DateAdd(Day,-7,GETDATE()),103) AS 'Required_Date',
ISNULL('10:00','') AS 'Required_Time_F', ---- Field to be created in sap
ISNULL('16:00','') AS 'Required_Time_T', ---- Field to be created in sap
Concat('Item ',Format(T1.DocEntry,'000')) as 'Item_Code',
Concat('Item ',Format(T1.DocEntry,'000'),' Of ',FORMAT(#NUM,'000')) as 'Item_Desc',
Concat(T0.DocNum,Format(T1.DocEntry,'000'),FORMAT(#NUM,'000')) AS 'package_id',
'1' AS Item_Qty,
T0.Weight/#NUM AS 'Item_Wgt',
'' AS 'Item_Cube',
convert(varchar,DateAdd(Day,-7,GETDATE()),103) AS 'Item_OnHand_Date'
FROM OINV T0 INNER JOIN INV12 T12 ON T0.DocEntry = T12.DocEntry INNER JOIN OCPR T5 ON T5.[CntctCode] = T0.[CntctCode] INNER JOIN ORDR T1 ON T1.DocNum <> T0.DocEntry
WHERE T0.DocNum > '1647674' AND T0.TrnspCode = 1
ORDER BY T1.DocEntry
This code worked perfectly when I was using WHERE T0.DocNum = '1647674' AND T0.TrnspCode = 1. As soon as I've changed it to a where clause with multiple DocNums it's falling flat on its face.
I'm thinking the easier option may be around me removing the Select Top and the Inner join on ORDR and OINV, and using my #NUM variable (from each document) to only actually give me the required number of lines instead of creating lots and filtering it down?
Any help will be appreciated as it took me a good couple of hours to get this to export in a format in which the CSV file takes with no issues!
---- EDIT ---- Update on the code, i think i've got somewhere close by tweaking a few things, i've actually changed the <> join on the ordr table, to a <= join relating to the variable i'm declaring..
I've ditched the variable number, and replaced it with a dynamic field within SAP.
SELECT
T0.[DocNum] AS 'Order_No',
ISNULL(T0.NumAtCard,'') AS 'Cust_Ord_No',
Convert(varchar,T0.DocDate,103) AS 'Order_Date',
'Delivery' AS 'Order_Type',
T0.CardName as 'Del_Name',
ISNULL(T12.buIldingS,'') AS 'Del_Addr_1',
ISNULL(T12.BlockS,'') AS 'Del_Addr_2',
ISNULL(T12.StreetS,'') AS 'Del_Addr_3',
ISNULL(T12.CityS,'') AS 'Del_Town',
ISNULL(T12.CountyS,'') AS 'Del_County',
ISNULL(T12.ZipCodeS,'') AS 'Postal_code',
ISNULL(T12.CountryS,'') AS 'Ctry_Code',
ISNULL(t5.Tel1,'') AS 'Contact_Tel_1',
ISNULL(T5.Tel2,'') AS 'Contact_Tel_2',
ISNULL(T5.Cellolar,'') AS 'Contat_Tel_Mob',
ISNULL(T5.E_mailL,'') AS 'Contact_Email',
ISNULL(T0.Comments,'') AS 'Order_Notes',
'00:05' AS 'Unload_Time',
'' AS 'Delivery_Method',
convert(varchar,DateAdd(Day,-7,GETDATE()),103) AS 'Required_Date',
ISNULL('10:00','') AS 'Required_Time_F', ---- Field to be created in sap
ISNULL('16:00','') AS 'Required_Time_T', ---- Field to be created in sap
Concat('Item ',Format(T1.DocEntry,'000')) as 'Item_Code',
Concat('Item ',Format(T1.DocEntry,'000'),' Of ',FORMAT(T0.U_PACKQTY,'000')) as 'Item_Desc',
Concat(T0.DocNum,Format(T1.DocEntry,'000'),FORMAT(T0.U_PACKQTY,'000')) AS 'package_id',
'1' AS Item_Qty,
T0.Weight/T0.U_PACKQTY AS 'Item_Wgt',
'' AS 'Item_Cube',
convert(varchar,DateAdd(Day,-7,GETDATE()),103) AS 'Item_OnHand_Date'--,
FROM OINV T0 INNER JOIN INV12 T12 ON T0.DocEntry = T12.DocEntry INNER JOIN OCPR T5 ON T5.[CntctCode] = T0.[CntctCode] INNER JOIN ORDR T1 ON T1.DocEntry <= T0.U_PACKQTY
WHERE T0.DocNum IN ('1647775','1647777','1647778')
The WHERE T0.DocNum IN will be replaced with another where clause to look for "Van Delivery" transport types, and DocNum which will be greater than previously exported DocNum, this will probably be by inserting the "MAX" DocNum into a table at the time i export it, and then using this "MAX" Docnum as a "Min" value in my where clause, so this should always look for new orders only. (I think!)
I think DocNum is of type varchar
I have two queries that should be joined together. Here is my query 1:
SELECT
t1.rec_id,
t1.category,
t1.name,
t1.code,
CASE
WHEN t1.name= 'A' THEN SUM(t1.amount)
WHEN t1.name = 'D' THEN SUM(t1.amount)
WHEN t1.name = 'H' THEN SUM(t1.amount)
WHEN t1.name = 'J' THEN SUM(t1.amount)
END AS Amount
FROM Table1 t1
GROUP BY t1.name, t1.rec_id, t1.category, t1.code
Query 1 produce this set of results:
Rec ID Category Name Code Amount
1 1 A MIX 70927.00
1 3 D MIX 19922.00
1 2 H MIX 55104.00
1 4 J MIX 76938.00
Then I have query 2:
SELECT
CASE
WHEN t2.category_id = 1 THEN SUM(t2.sum)
WHEN t2.category_id = 2 THEN SUM(t2.sum)
WHEN t2.category_id = 3 THEN SUM(t2.sum)
WHEN t2.category_id = 4 THEN SUM(t2.sum)
END AS TotalSum
FROM Table2 t2
INNER JOIN Table1 t1
ON t1.amnt_id = t2.amnt_id
AND t2.unique_id = #unique_id
GROUP BY t2.category_id
The result set of query 2 is this:
TotalSum
186013.00
47875.00
12136.00
974602.00
All I need is this result set that combines query 1 and query 2:
Rec ID Category Name Code Amount TotalSum
1 1 A MIX 70927.00 186013.00
1 3 D MIX 19922.00 47875.00
1 2 H MIX 55104.00 12136.00
1 4 J MIX 76938.00 974602.00
As you can see there is connection between table 1 and table 2. That connection is amnt_id. However, I tried doing LEFT INNER JOIN on query 1 and then simply using same logic with case statement to get the total sum for table 2. Unfortunately Sybase version that I use does not support Left Inner Join. I'm wondering if there is other way to join these two queries? Thank you
I wondered if the CASE statement makes sense in the first query because it sums in every row. Are there other values for the name column except A, D, H, J? If not you can change the CASE statement to SUM(t1.amount) AS Amount. Also the GROUP BY in the first query seems dubious to me: you are grouping by the record id column - that means you are not grouping at all but instead return every row. If that is what you really want you can omit the SUM at all and just return the pure amount column.
As far as I understood your problem and your data structure: the values in Table2 are kind of category sums and the values in Table1 are subsets. You would like to see the category sum for every category in Table1 next to the single amounts?
You would typically use a CTE (common table expression, "WITH clause") but ASE doesn't support CTEs, so we have to work with joins. I recreated your tables in my SQL Anywhere database and put together this example. In a nutshell: both queries are subqueries in an outer query and are left joined on the category id:
SELECT *
FROM
(
SELECT
t1.rec_id,
t1.category,
t1.name,
t1.code,
CASE
WHEN t1.name= 'A' THEN SUM(t1.amount)
WHEN t1.name = 'D' THEN SUM(t1.amount)
WHEN t1.name = 'H' THEN SUM(t1.amount)
WHEN t1.name = 'J' THEN SUM(t1.amount)
END AS Amount
FROM Table1 t1
GROUP BY t1.rec_id, t1.name, t1.category, t1.code
) AS t1
LEFT JOIN
(
SELECT category_id, SUM(sum) FROM
table2
GROUP BY category_id
) AS totals(category_id, total_sum)
ON totals.category_id = t1.category;
This query gives me:
Rec ID Category Name Code Amount Category_id total_sum
2 3 D MIX 19922.00 3 47875.00
3 2 H MIX 55104.00 2 12136.00
1 1 A MIX 70927.00 1 186013.00
4 4 J MIX 76938.00 4 974602.00
You surely have to tweak it a bit including your t2.unique_id column (that I don't understand from your queries) but this is a practical way to work around ASE's missing CTE feature.
BTW: it's either an INNER JOIN (only the corresponding records from both tables) or a LEFT (OUTER) JOIN (all from the left, only the corresponding records from the right table) but a LEFT INNER JOIN makes no sense.
I'm wondering if there is a way to have a complete select statement and if no row is returned to return a row with a default value in a specific field (SQL Server)? Here's a generic version of my SQL to better explain:
SELECT COUNT(CASE WHEN CAST(c.InjuryDate as DATE)>DATEADD(dd,-60, getdate ()) THEN b.InjuryID end) InjuryCount, a.PersonID
FROM Person_Info a
JOIN Injury_Subject b on b.PersonID=a.PersonID
JOIN Injury_Info c on c.InjuryID=b.InjuryID
WHERE EXISTS (SELECT * FROM Hospital_Record d WHERE d.PersonID=b.PersonID and d.InjuryID=b.InjuryID) --There could be multiple people associated with the same InjuryID
GROUP BY a.PersonID
If NOT EXISTS (SELECT * FROM Hospital_Record d WHERE d.PersonID=a.PersonID) THEN '0' in InjuryCount
I want a row for each person who has had an injury to display. Then I'd like a count of how many injuries resulted in hospitalizations in the last 60 days. If they were not hospitalized, I'd like the row to still be generated, but display '0' in InjuryCount column. I've played with this a bunch, moving my date from the WHERE to the SELECT, trying IF ELSE combos, etc. Could someone help me figure out how to get what I want please?
It's hard to tell without an example of input and desired output, but I think this is what you are going for:
select
InjuryCount = count(case
when cast(ii.InjuryDate as date) > dateadd(day,-60,getdate())
then i.InjuryId
else null
end
)
, p.PersonId
from Person_Info p
left join Hosptal_Record h on p.PersonId = h.PersonId
left join Injury_Subject i on i.PersonId = h.PersonId
and h.InjuryId = i.InjuryId
left join Injury_Info ii on ii.InjuryId = i.InjuryId
group by p.PersonId;
Goal:
To know if we have purchased duplicate StockCodes or Stock Description more than once on difference purchase orders
So, if we purchase Part ABC on Purchase Order 1 and Purchase Order 2, it should return the result of
PurchaseOrders, Part#, Qty
Purchase Order1, Purchase Order2, ABC, 2
I just don't know how to pull the whole code together, more to the point, how do I know if it's occurred on more than 1 Purchase Order without scrolling through all the results , may also have to do with Multiple (Having Count) Statements as I only seem to be doing by StockCode
SELECT t1.PurchaseOrder,
t1.MStockCode,
Count(t1.MStockCode) AS SCCount,
t1.MStockDes,
Count(t1.MStockDes) AS DescCount
FROM PorMasterDetail t1
INNER JOIN PorMasterHdr t2
ON t1.PurchaseOrder = t2.PurchaseOrder
WHERE Year(t2.OrderEntryDate) = Year(Getdate())
AND Month(t2.OrderEntryDate) = Month(Getdate())
GROUP BY t1.PurchaseOrder,
t1.MStockCode,
t1.MStockDes
HAVING Count(t1.MStockCode) > 1
Using responses I came up with the following
select * from
(
SELECT COUNT(dbo.InvMaster.StockCode) AS Count, dbo.InvMaster.StockCode AS StockCodes,
dbo.PorMasterDetail.PurchaseOrder, dbo.PorMasterHdr.OrderEntryDate
FROM dbo.InvMaster INNER JOIN dbo.PorMasterDetail ON
dbo.InvMaster.StockCode = dbo.PorMasterDetail.MStockCode
INNER JOIN dbo.PorMasterHdr ON dbo.PorMasterDetail.PurchaseOrder = dbo.PorMasterHdr.PurchaseOrder
WHERE YEAR(dbo.PorMasterHdr.OrderEntryDate) = YEAR(GETDATE())
GROUP BY dbo.InvMaster.StockCode, dbo.InvMaster.StockCode,
dbo.PorMasterDetail.PurchaseOrder, dbo.PorMasterHdr.OrderEntryDate
) Count
Where Count.Count > 1
This returns the below , which is starting to be a bit more helpful
In result line 2,3,4 we can see the same stock code (*30044) ordered 3 times on different
purchase orders.
I guess the question is, is it possible to look at If something was ordered more than once within say a 30 day period.
Is this possible?
Count StockCodes PurchaseOrder OrderEntryDate
2 *12.0301.0021 322959 2014-09-08
2 *30044 320559 2014-01-21
8 *30044 321216 2014-03-26
4 *30044 321648 2014-05-08
5 *32317 321216 2014-03-26
4 *4F-130049/TEST 323353 2014-10-22
5 *650-1157/E 322112 2014-06-24
2 *650-1757 321226 2014-03-27
SELECT *
FROM
(
SELECT h.OrderEntryDate, d.*,
COUNT(*) OVER (PARTITION BY d.MStockCode) DupeCount
FROM
PorMasterHdr h
INNER JOIN PorMasterDetail d ON
d.PurchaseOrder = h.PurchaseOrder
WHERE
-- first day of current month
-- http://blog.sqlauthority.com/2007/05/13/sql-server-query-to-find-first-and-last-day-of-current-month/
h.OrderEntryDate >= CONVERT(VARCHAR(25), DATEADD(dd,-(DAY(GETDATE())-1),GETDATE()),101)
) dupes
WHERE
dupes.DupeCount > 1;
This should work if you're only deduping on stock code. I was a little unclear if you wanted to dedupe on both stock code and stock desc, or either stock code or stock desc.
Also I was unclear on your return columns because it almost looks like you're wanting to pivot the columns so that both purchase order numbers appear on the same line.