SQL Server : outputting duplicates - sql

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;

Related

Include indicator of data present, but not include in results

I can't quite explain it properly, hence the awful title.
As an example, I would have a query that shows customer complaints, of a certain type, and the dates since those complaints. Something like the following:
select * from( select c.firstname, c.lastname , max(ont.date1) as "LastDate",
DATEDIFF(DAY, MAX(ont.Date1), SYSDATETIME()) AS "Days"
from [ComplaintInfo] ci
inner join [OrderNotes] ont on ont.orderid = ci.orderid
inner join [Customers] c on c.custid = ci.custid
right outer join [CustLive] cl on ont.custidl = cl.custidl
where (ci.typeofcomp = '2' or ci.typeofcomp = '3')
and (ont.answertype <> '2' and ont.answertype <> '3' and ont.answertype <>'4'
group by c.lastname, c.firstname
) Sub
where Days >= 5
order by Days, sub.lastname asc
This would give something like
John | Smith | 2020-06-03T13:00:00 | 1
Terry | Jones | 2020-05-04T:04:00:00 | 30
However, although I'm wanting typeoforder to NOT be 2 or 3, and I don't want them to be included in my result set, I would like to know whether or not there have been any orders of those types. So, if for example
John | Smith |2020-06-03T13:00:00 | 1 | Yes
Terry | Jones | 2020-05-04-04:00:00 | 30 | No
Could just be asking a stupid question, but need to know. Thanks.
You can remove the condition from the where clause and use a conditional expression:
select
c.firstname,
c.lastname,
max(ont.date1) as LastDate
datediff(day, max(ont.date1), sysdatetime()) as days,
max(case when ci.typeofcomp = 2 or ci.typeofcomp = 3 then 'Yes' else 'No' end) hasTypeOfComp_2_Or_3
from [ComplaintInfo] ci
inner join [OrderNotes] ont on ont.orderid = ci.orderid
inner join [Customers] c on c.custid = ci.custid
right outer join [CustLive] cl on ont.custidl = cl.custidl
where ont.answertype <> 2 and ont.answertype <> 3 and ont.answertype <> 4
group by c.lastname, c.firstname
Notes:
I removed the single quotes around the numbers - since they look like numbers, I assumed that they are stored as such, in which case you want to compare them against literal numbers (if that's not the case, you can put the quotes back)
I am suspicious about the right outer join: is this really what you want? If not, just use an inner join

Selecting table to use in FROM clause based on certain condition

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.

Manipulating SQL table

I have a table, the structure of which I have simplified to the smaller table below.
I want to manipulate the dataset below into the following form:
The new dataset will contain a single record for each case of DC, with a yes/no flag indicating if the NatureOfTumour has changed from DC to IN, and the time taken to change from DC to IN if applicable.
The change from DC to IN will be considered only if location has remained the same i.e. only those records should be considered where NatureOfTumour has changed from DC to IN and the location remained the same. ItemNo is the unique ID.
On a community member's advice I have pasted the table in text below as well, cleaned up as best as I could. The last column "Gen" is empty. ItemNo is the unique ID. Copying the text below to excel and doing a text-to-columns (separated by spaces) should give you the original table in a readable format. Sorry cant think of a better way to paste the table here.
ItemNo DateOfTest NatureOfTumour Location Centre Gen
2345 07/2006 DC P S-224
2345 12/2006 IN P S-224
2342 05/2004 DC Q B-266
3878 06/2006 DC P S-224
3878 05/2005 DC Q S-224
5678 09/2000 IN P S-224
5597 10/2001 DC P B-266
5597 01/1999 IN Q B-266
Try this. The LEAD function looks at the next row based on groups of ItemNo ordered by DateOfTest.
WITH abc AS (
SELECT
ItemNo
,DateOfTest
,NatureOfTumour
,Location
,Centre
,LEAD(NatureOfTumour) OVER (PARTITION BY ItemNo ORDER BY DateOfTest) as FutureNature
,LEAD(Location) OVER (PARTITION BY ItemNo ORDER BY DateOfTest) as FutureLocation
,LEAD(DateOfTest) OVER (PARTITION BY ItemNo ORDER BY DateOfTest) as FutureDateOfTest
FROM test_results
)
SELECT
ItemNo
,DateOfTest
,NatureOfTumour
,CASE
WHEN FutureNature = 'IN'
AND FutureLocation = Location
THEN 'Yes'
ELSE 'NO'
END AS State_Change
,FutureDateOfTest - DateOfTest as Date_Diff
,Location
,Centre
from abc
WHERE NatureOfTumour = 'DC'
You need a self join. Something along these lines:
SELECT
d.ItemNo,
i.DateOfTest - d.DateOfTest AS datediff,
d.Location,
d.Centre,
d.Gen
FROM
(
SELECT
*
FROM demo
WHERE NatureOfTumour = 'DC'
) d
INNER JOIN
(
SELECT
*
FROM demo
WHERE NatureOfTumour = 'IN'
) i ON d.ItemNo = i.ItemNo
AND d.Location = i.Location;
If I understood your question, you could try this:
Let me know .
If you want in output only the rows who changed (GEN='Y'), change LEFT JOIN to INNER JOIN.
SELECT A.ITEMNO, A.DATEOFTEST, A.NATUREOFTUMOUR, A.LOCATION
, CASE WHEN B.NATUREOFTUMOUR='IN' AND A.LOCATION = B.LOCATION THEN 'Y' ELSE 'N' END AS GEN_NEW
, CASE WHEN B.NATUREOFTUMOUR='IN' AND A.LOCATION = B.LOCATION THEN B.DATEOFTEST-A.DATEOFTEST END AS TIME_PASS
FROM TE A
LEFT JOIN TE B ON A.ITEMNO=B.ITEMNO AND B.NATUREOFTUMOUR<>'DC' AND A.DATEOFTEST < B.DATEOFTEST
WHERE A.NATUREOFTUMOUR='DC
OR (I can't understand from your question)
SELECT A.ITEMNO, A.DATEOFTEST, A.NATUREOFTUMOUR, A.LOCATION
, CASE WHEN B.NATUREOFTUMOUR='IN' THEN 'Y' ELSE 'N' END AS GEN_NEW
, CASE WHEN B.NATUREOFTUMOUR='IN' THEN B.DATEOFTEST-A.DATEOFTEST END AS TIME_PASS
FROM TE A
LEFT JOIN TE B ON A.ITEMNO=B.ITEMNO AND B.NATUREOFTUMOUR<>'DC' AND A.DATEOFTEST < B.DATEOFTEST AND A.LOCATION = B.LOCATION
WHERE A.NATUREOFTUMOUR='DC'\\
Output
ITEMNO DATEOFTEST NATUREOFTUMOUR LOCATION GEN_NEW TIME_PASS
1 2345 01.07.2006 DC P Y 153
2 2342 01.06.2006 DC Q N NULL
3 5597 01.10.2001 DC P N NULL
4 3878 01.05.2005 DC Q N NULL
5 3878 01.06.2006 DC P N NULL

Combining data into a single cell while summing a seperate cell

I don't know how to word my question very well, so I will start with the data being returned:
prop_id | assessed_value | sale_id
35004 | 401200 | 1920831
35005 | 40500 | 1920831
35023 | 11300 | 1920831
34380 | 139100 | 1915846
127959 | 286400 | 1915882
I would like it to return:
prop_id | assessed_value | sale_id
35004, 35005, 35023 | 453000(Sum of the 3 parcels) | 1920831
34380 | 139100 | 1915846
127959 | 286400 | 1915882
So my main goal is to combine all the parcels on the sale_id field into a string, but only when a sale id has multiple prop_id, and then sum up the assessed value of all those. This is the query I am using to get the first set of data...
select
pv1.[prop_id],
pv1.[assessed_val],
ld1.[sale_id]
from dbo.land_detail as ld1
join dbo.property_val as pv1 on
pv1.[prop_id] = ld1.[prop_id] and
pv1.[prop_val_yr] = ld1.[prop_val_yr] and
pv1.[sup_num] = ld1.[sup_num]
left join dbo.sale as sale1 on
sale1.[chg_of_owner_id] = ld1.[sale_id]
where
pv1.[prop_inactive_dt] is null
order by sale_id, prop_id
prop_id is of data type INT
assessed_val is of data type numeric(14,0)
sale_id is of data type INT
You can use WITH to create a temporary table then you can use STUFF() FOR XML PATH to concatenate the prop_id field into one row
WITH temp AS (
select pv1.[prop_id],
pv1.[assessed_val],
ld1.[sale_id],
sale1.[sl_price],
sale1.[sl_dt],
(pv1.[assessed_val]/NULLIF(sale1.[sl_price],0) as Ratio
from dbo.land_detail as ld1 with(nolock)
join dbo.property_val as pv1 with(nolock)
on pv1.[prop_id] = ld1.[prop_id]
and pv1.[prop_val_yr] = ld1.[prop_val_yr]
and pv1.[sup_num] = ld1.[sup_num]
left join dbo.sale as sale1 with(nolock)
on sale1.[chg_of_owner_id] = ld1.[sale_id]
where sale1.sl_dt <= '04/30/16'
and sale1.sl_dt >= '05/01/15'
and pv1.[sub_type] = 'r'
and pv1.[prop_val_yr] = 2016
and pv1.[prop_inactive_dt] is null
order by sale_id, prop_id
)
SELECT STUFF(( SELECT ', ' + CAST(prop_id AS VARCHAR)
FROM temp
WHERE sale_id = t.sale_id
FOR XML PATH(''),TYPE)
.value('.','NVARCHAR(MAX)'),1,2,'') AS parcels,
SUM(t.assessed_val) assessed_val,
t.sale_id,
t.sl_price,
t.sl_dt,
SUM(t.ratio) ratio
FROM temp t
GROUP BY t.sale_id,
t.sl_price,
t.sl_dt
ORDER BY t.sale_id DESC
Put everything into a temp table (#temp) as below. Then make a self join to select the values. There are several ways of doing concatenation using (SUFF(), XML PATH, etc). Search on the internet on how to concatenate rows into comma separated string refer to this link for example
select
pv1.[prop_id],
pv1.[assessed_val],
ld1.[sale_id],
sale1.[sl_price],
sale1.[sl_dt],
(pv1.[assessed_val]/sale1.[sl_price]) as Ratio
--------Temp Table------------
INTO #temp
------------------------------
from dbo.land_detail as ld1 with(nolock)
join dbo.property_val as pv1 with(nolock) on
pv1.[prop_id] = ld1.[prop_id] and
pv1.[prop_val_yr] = ld1.[prop_val_yr] and
pv1.[sup_num] = ld1.[sup_num]
left join dbo.sale as sale1 with(nolock) on
sale1.[chg_of_owner_id] = ld1.[sale_id]
where
sale1.sl_dt <= '04/30/16' and
sale1.sl_dt >= '05/01/15' and
pv1.[sub_type] = 'r' and
pv1.[prop_val_yr] = 2016 and
pv1.[prop_inactive_dt] is null
order by sale_id, prop_id

Combine results of two different records in one

I have an SQL data stored in a single table but with different type and I want to combine the result in one liner show in the example below. Anyone can give suggestion to this SQL query?
My SQL query to get this results:
SELECT NumValue, Label, Type
FROM (CustomPollerStatusTable
INNER JOIN CustomPollers ON (CustomPollerStatusTable.PollerID =
CustomPollers.CustomPollerID))
INNER JOIN Nodes ON (CustomPollerStatusTable.NodeID = Nodes.NodeID)
WHERE ((Nodes.Caption = 'fqdn') AND
(CustomPollers.Type = 'Student Info') AND
(CustomPollerStatusTable.NumValue > 89) AND
(NOT (CustomPollerStatusTable.Label LIKE '%.snapshot%')) AND
(NOT (CustomPollerStatusTable.Label LIKE '%aggr%')) AND
(NOT (CustomPollerStatusTable.Label = '/vol/scratch/'))
)
====================================
NumValue | Label | Type
====================================
90 | Student 1 | Student Info
10 | Student 1 | Student Class
====================================
The results that I would like to achieve:
==========================================================================
NumValue.Info | NumValue.Class | Label | Type.Info | Type.Class
==========================================================================
90 | 10 | Student 1 | Student Info | Student Class
==========================================================================
This should do it. Slightly different from nrathaus' answer. Obviously you can use as to change the column names to whatever you want.
select
s1.NumValue,
s2.NumValue,
s1.Label,
s1.Type,
s2.Type
from Student s1 inner join Student s2
on s1.Label = s2.Label
where s1.Type like '%Info'
and s2.Type like '%Class'
EDIT: Now that you have posted your select statement I think this might not work. Can you post the table structure?
EDIT2: This might work.
INSERT INTO TempTable(
NumValInfo,
Label,
TypeInfo)
SELECT
c.NumValue,
c.Label,
p.Type
FROM (CustomPollerStatusTable c INNER JOIN CustomPollers p
ON (c.PollerID = p.CustomPollerID))
INNER JOIN Nodes n
ON (c.NodeID = n.NodeID)
WHERE n.Caption = 'fqdn'
AND p.Type = 'Student Info'
AND c.NumValue > 89
AND NOT (c.Label LIKE '%.snapshot%')
AND NOT (c.Label LIKE '%aggr%')
AND NOT (c.Label = '/vol/scratch/')
AND p.Type like '%Info'
UPDATE TempTable set
NumValClass = c.NumValue,
TypeClass = p.Type
FROM (CustomPollerStatusTable c INNER JOIN CustomPollers p
ON (c.PollerID = p.CustomPollerID))
INNER JOIN Nodes n
ON (c.NodeID = n.NodeID)
INNER JOIN TempTable t
ON t.Label = c.Label
WHERE n.Caption = 'fqdn'
AND p.Type = 'Student Info'
AND c.NumValue > 89
AND NOT (c.Label LIKE '%.snapshot%')
AND NOT (c.Label LIKE '%aggr%')
AND NOT (c.Label = '/vol/scratch/')
AND p.Type like '%Class'
SELECT * FROM TempTable
I think this will do:
SELECT tblInfo.NumValue AS `NumValue.Info`,
tblClass.NumValue AS `NumValue.Class`,
Table.Label AS Label,
tblInfo.Type AS `Type.Info`,
tblClass.Type AS `Type.Class`
FROM (Table)
LEFT JOIN Table AS tblInfo ON tblInfo.Label = Table.Label ON tblInfo.Type = 'Student Info'
LEFT JOIN Table AS tblClass ON tblClass.Label = Table.Label ON tblClass.Type = 'Student Class'
GROUP BY Table.Label