I have a script when I'm trying to select locations in an inventory where quantity of said location is <= 5. The query is complete, now I'm trying to do some fine tuning, and what I'm running into now is when I use the distinct clause I am still receiving duplicate records in the same column. I do know the column next to the first are unique, but I thought distinguishing distinct and one column would roll over to next related to said column.
Here is my code:
select DISTINCT bin.scannable_id as bin,
bi.bin_id as case1,
pallet.scannable_id as pallet,
-- bi.isbn as fcsku,
nvl(fs.asin,bi.isbn) as asin,
sum(bi.quantity) as quantity,
pallet.creation_date as received_date
from containers bin
join containers pallet on pallet.containing_container_id = bin.container_id
join containers case on case.containing_container_id = pallet.container_id
join bin_items bi on bi.container_id = case.container_id
left join fcskus fs on fs.fcsku = bi.isbn
where bin.scannable_id like 'R-1-T%'
and bi.quantity <= '5'
group by bin.scannable_id, pallet.scannable_id, bi.bin_id, bi.owner,bi.isbn,nvl(fs.asin,bi.isbn), pallet.creation_date
order by sum(bi.quantity);
My output, which is obviously showing duplicate records in the scannable_id column:
Correct Formatting Thanks to conrad.
select DISTINCT bin.scannable_id as bin,
pallet.scannable_id as pallet,
nvl(fs.asin,bi.isbn) as asin,
sum(bi.quantity) as quantity
from containers bin
join containers pallet on pallet.containing_container_id = bin.container_id
join containers case on case.containing_container_id = pallet.container_id
join bin_items bi on bi.container_id = case.container_id
left join fcskus fs on fs.fcsku = bi.isbn
where bin.scannable_id like 'R-1-T%'
having sum(bi.quantity) <= '5'
group by bin.scannable_id, pallet.scannable_id, nvl(fs.asin,bi.isbn), bi.quantity
order by sum(bi.quantity);
As said on the comments you dont need a DISTINCT if you have the group by statement. And format your date field because depending on your oracle client configuration it will not show you the entire date format (e.g. date time). So try with this:
select bin.scannable_id as bin,
bi.bin_id as case1,
pallet.scannable_id as pallet,
nvl(fs.asin,bi.isbn) as asin,
to_char(pallet.creation_date, 'yyyy-mm-dd') as received_date
sum(bi.quantity) as quantity,
from containers bin
join containers pallet on pallet.containing_container_id = bin.container_id
join containers case on case.containing_container_id = pallet.container_id
join bin_items bi on bi.container_id = case.container_id
left join fcskus fs on fs.fcsku = bi.isbn
where bin.scannable_id like 'R-1-T%'
and bi.quantity <= '5'
group by bin.scannable_id,
pallet.scannable_id,
bi.bin_id,
bi.owner,
bi.isbn,
nvl(fs.asin,bi.isbn),
to_char(pallet.creation_date, 'yyyy-mm-dd')
order by sum(bi.quantity);
bi.bin_id is different for each row, so you do only have distinct results in your resultset.
distinct is applied to the final visible resultset (once the to_char etc. functions are processed)
distinct is redundant if you already use a group by expression
Solution: skipp the bi.bin_id column from your select expression.
Your logic is also confusing. You want to know the SUM of all the bi.* elements. To do so you cannot group by bi.bin_id nor any field from the bi table. This is the reason why your quantity result is always 1.
Related
I am working on a database of a large retail store.
I have to query data from multiple tables to get numbers such as revenue, raw proceeds and compare different time periods.
Most of it is quite easy but I was struggling to work out a way of joining multiple CTEs.
I made a fiddle so you know what I am talking about.
I simplified the structure a lot and left out quite a few columns in the subqueries because they do not matter in this case.
As you can see every row in every table has country and brand in it.
The final query has to be grouped by those.
What I first tried was to FULL JOIN all the tables, but that didn't work in some cases as you can see here: SQLfiddle #1. Note the two last rows which did not group correctly.
Select Coalesce(incoming.country, revenue.country, revcompare.country,
openord.country) As country,
Coalesce(incoming.brand, revenue.brand, revcompare.brand,
openord.brand) As brand,
incoming.OrdersNet,
openord.OpenOrdersNet,
revenue.Revenue,
revenue.RawProceeds,
revcompare.RevenueCompare,
revcompare.RawProceedsCompare
From incoming
Full Join openord On openord.country = incoming.country And
openord.brand = incoming.brand
Full Join revenue On revenue.country = incoming.country And
revenue.brand = incoming.brand
Full Join revcompare On revcompare.country = incoming.country And
revcompare.brand = incoming.brand
Group By incoming.OrdersNet,
openord.OpenOrdersNet,
revenue.Revenue,
revenue.RawProceeds,
revcompare.RevenueCompare,
revcompare.RawProceedsCompare,
incoming.country,
revenue.country,
openord.country,
revcompare.country,
incoming.brand,
revenue.brand,
revcompare.brand,
openord.brand
Order By country,
brand
I then rewrote the query keeping all the CTEs. I added another CTE (basis) which UNIONs all the possible country and brand combinations and left joined on that one.
Now it works fine (check it out here -> SQLfiddle #2) but it just seems so complicated. Isn't there an easier way to achieve this? The only thing I probably won't be able to change are the CTEs as in real life they are way more complex.
WITH basis AS (
SELECT Country, Brand FROM incoming
UNION
SELECT Country, Brand FROM openord
UNION
SELECT Country, Brand FROM revenue
UNION
SELECT Country, Brand FROM revcompare
)
SELECT
basis.Country,
basis.Brand,
incoming.OrdersNet,
openord.OpenOrdersNet,
revenue.Revenue,
revenue.RawProceeds,
revcompare.RevenueCompare,
revcompare.RawProceedsCompare
FROM basis
LEFT JOIN incoming On incoming.Country = basis.Country AND incoming.Brand = basis.Brand
LEFT JOIN openord On openord.Country = basis.Country AND openord.Brand = basis.Brand
LEFT JOIN revenue On revenue.Country = basis.Country AND revenue.Brand = basis.Brand
LEFT JOIN revcompare On revcompare.Country = basis.Country AND revcompare.Brand = basis.Brand
Thank you all for your help!
Since you only work with two tables, orders and rev, consider conditional aggregation by moving WHERE conditions to CASE logic for single aggregate query. Also, consider only one CTE for all possible country/brand pairs for LEFT JOIN on the two tables.
WITH cb AS (
SELECT Country, Brand FROM orders
UNION
SELECT Country, Brand FROM rev
)
SELECT cb.Country
, cb.Brand
, SUM(o.netprice) AS OrdersNet
, SUM(CASE
WHEN o.isopen = 1
THEN o.netprice
END) AS OpenOrdersNet
, SUM(CASE
WHEN r.bdate BETWEEN '2020-12-01' AND '2020-12-31'
THEN r.netprice
END) AS Revenue
, SUM(CASE
WHEN r.bdate BETWEEN '2020-12-01' AND '2020-12-31'
THEN r.rpro
END) AS RawProceeds
, SUM(CASE
WHEN r.bdate BETWEEN '2020-11-01' AND '2020-11-30'
THEN r.netprice
END) AS RevenueCompare
, SUM(CASE
WHEN r.bdate BETWEEN '2020-11-01' AND '2020-11-30'
THEN r.rpro
END) AS RawProceedsCompare
FROM cb
LEFT JOIN orders o
ON cb.Country = o.Country
AND cb.Brand = o.Brand
LEFT JOIN rev r
ON cb.Country = r.Country
AND cb.Brand = r.Brand
GROUP BY cb.Country
, cb.Brand
SQL Fiddle
I have an issue on my SQL query. I tried doing two ways
With the first query I got the right amount but I lose some name descriptions
With the second I got every name descriptions but I got a lower amount.
Context: I want to get the revenue gotten between two dates.
I need the following columns from tables
Table reciboDet I need the columns CtaIngreso, ValorUnitReciboDet
Table CuentaIngreso_A the column nombrectaingreso, ctaingreso (only to create the join)
Table Recibo the columns FechaRecibo and ReciboAnulado
To get the right name descriptions I need to verify the receipt year that was in the table AvpgEnc, but when I do that a lose the amount.
First query
SELECT
ReciboDet.CtaIngreso
, SUM(ReciboDet.ValorUnitReciboDet) AS Total
, CuentaIngreso_A.NombreCtaIngreso
FROM
ReciboDet
INNER JOIN CuentaIngreso_A
ON ReciboDet.CtaIngreso = CuentaIngreso_A.CtaIngreso
WHERE
(ReciboDet.NumRecibo IN
(SELECT NumRecibo
FROM Recibo
WHERE (FechaRecibo BETWEEN '01/10/2020' AND '31/10/2020')
AND (ReciboAnulado = 0)
AND (CuentaIngreso_A.Anio = DATEPART(year, FechaRecibo))
)
)
GROUP BY
ReciboDet.CtaIngreso
, CuentaIngreso_A.NombreCtaIngreso
ORDER BY
CuentaIngreso_A.NombreCtaIngreso
Second query
SELECT
ReciboDet.CtaIngreso [cuenta],
sum(ReciboDet.ValorUnitReciboDet) [monto],
CuentaIngreso_A.NombreCtaIngreso [descripcion]
FROM
ReciboDet
inner join avpgenc
on ReciboDet.NumFactura = AvPgEnc.NumAvPg
inner join CuentaIngreso_A
on ReciboDet.CtaIngreso = CuentaIngreso_A.CtaIngreso
WHERE
(ReciboDet.NumRecibo IN
(SELECT NumRecibo
FROM Recibo
WHERE (FechaRecibo BETWEEN '01/10/2020' AND '31/10/2020')
AND (ReciboAnulado = 0)
)
AND (year(AvPgEnc.FechaVenceAvPg) = CuentaIngreso_A.Anio)
)
GROUP BY
ReciboDet.CtaIngreso
, CuentaIngreso_A.NombreCtaIngreso
ORDER BY
ReciboDet.CtaIngreso
I have two tables that I need to combine, but I can't get to seem the joins to work. The picture has three tables
_date_array2: Has a field DateMonthYr that contains all possible date/yr combinations
_sales_summ_tbl__ => Has 5 fields. Only months with sales show up. For example, you see only three records showing up for the second table. There is no 5/2016, for example, because there were no sales for that month.
My goal is to "pad" the second table to have TotalDemand of 0's for months with no sales. I am very close (see the third table), except I cannot get the PartNumber to show up for dates with no sales.
My guess is that it's due to the RIGHT JOIN. But I'm not sure how to handle this. The output I am hoping for is table 3 but with the part number populated for all entries.
And here is my code (the results from running this code are the third/last table in the picture):
SELECT TmpSalesTbl.PartNumber as PartNumber,
tmp_date_array.CreateDateMonth as CreateDateMonth,
tmp_date_array.CreateDateYear as CreateDateYear,
CASE WHEN TmpSalesTbl.TotalDemand is NULL THEN 0 ELSE TmpSalesTbl.TotalDemand END as TotalDemand
FROM #_sales_summ_tbl__ TmpSalesTbl
RIGHT JOIN #_date_array2 tmp_date_array on tmp_date_array.CreateDateMonthYr = TmpSalesTbl.CreateDateMonthYr
ORDER BY tmp_date_array.CreateDateYear, tmp_date_array.CreateDateMonth
It is more conventional to place the list of all dates first, then left join to the data and whilst using a case expression is fine an alternative is coalesce(). This should ensure all the wanted months/years display:
SELECT
tmpsalestbl.PartNumber AS partnumber
, tmp_date_array.CreateDateMonth AS createdatemonth
, tmp_date_array.CreateDateYear AS createdateyear
, COALESCE(tmpsalestbl.TotalDemand, 0) AS totaldemand
FROM #_date_array2 tmp_date_array
LEFT JOIN #_sales_summ_tbl__ tmpsalestbl ON tmp_date_array.CreateDateMonthYr = tmpsalestbl.CreateDateMonthYr
ORDER BY
tmp_date_array.CreateDateYear
, tmp_date_array.CreateDateMonth
To populate for evey partnumber, on every month, you will need a new subquery:
select distinct PartNumber #_sales_summ_tbl__
And then cross join that to the years/months so you have a complete set of years/months/parts.
SELECT
cj.PartNumber AS partnumber
, tmp_date_array.CreateDateMonth AS createdatemonth
, tmp_date_array.CreateDateYear AS createdateyear
, COALESCE(tmpsalestbl.TotalDemand, 0) AS totaldemand
FROM #_date_array2 tmp_date_array
CROSS JOIN (
SELECT DISTINCT
PartNumber FROM #_sales_summ_tbl__
) cj
LEFT JOIN #_sales_summ_tbl__ tmpsalestbl ON tmp_date_array.CreateDateMonthYr = tmpsalestbl.CreateDateMonthYr
AND cj.PartNumber = tmpsalestbl.PartNumber
ORDER BY
tmp_date_array.CreateDateYear
, tmp_date_array.CreateDateMonth
;
SELECT
a.ItemCode,
SUM(a.NoOfApplication) AS NoOfApplication,
SUM(a.NoOfAccomplished) AS NoOfAccomplished,
SUM(a.NoOfPending) AS NoOfPending,
SUM(a.NoOfDocumentCompliance) AS NoOfDocumentCompliance,
a.[Year]
FROM
(SELECT
ItemCode,
COUNT(am.ReferenceNumber) AS NoOfApplication,
COUNT(TNA.NoOfAccomplished) AS NoOfAccomplished,
COUNT(TNP.NoOfPending) AS NoOfPending,
SUM(FDC.NoOfDocumentCompliance) AS NoOfDocumentCompliance,
DATENAME(month, ad.applicationdate) AS [Year]
FROM
AppTypes at
INNER JOIN
AssessmentMainDetails am ON at.Category = am.Category
INNER JOIN
InspectionProcesses i ON am.ReferenceNumber = i.ReferenceNo
LEFT JOIN
(SELECT
COUNT(Status) AS NoOfDocumentCompliance,
ReferenceNumber, Status
FROM
ApplicationStatus
WHERE
Status = 'For Document Compliance'
GROUP BY
ReferenceNumber, Status) AS FDC ON FDC.ReferenceNumber = i.ReferenceNo
LEFT JOIN
(SELECT
COUNT(ReferenceNo) AS NoOfAccomplished,
ReferenceNo
FROM
InspectionProcesses
WHERE
DateOfInspection <> ''
GROUP BY
ReferenceNo) AS TNA ON TNA.ReferenceNo = i.ReferenceNo
LEFT JOIN
(SELECT
COUNT(ReferenceNo) AS NoOfPending, ReferenceNo
FROM
InspectionProcesses
WHERE
DateOfInspection = ''
GROUP BY
ReferenceNo) AS TNP ON TNP.ReferenceNo = i.ReferenceNo
INNER JOIN
ApplicationDetails ad on i.ReferenceNo = ad.ReferenceNumber
INNER JOIN
Companies c on ad.CompanyId = c.CompanyID
INNER JOIN
Zones z on c.zonecode = z.zonecode
INNER JOIN
ZoneGroups zg on z.ZoneGroup = zg.ZoneGroupId
WHERE
DateOfInspection = ''
AND ad.ApplicationDate BETWEEN '2017-08-01' AND '2017-09-30'
AND zg.ZoneGroupCode = 'HO'
AND z.ZoneCode = 'VIDC'
GROUP BY
ItemCode, DATENAME(month, ad.applicationdate)) a
GROUP BY
a.ItemCode, a.[Year]
This my code, I already converted my date to get the month name. Please I need help
Look carefully. That giant derived table (a - nice meaningful name btw) has the same group by clause as the outermost query. So that means that [a] has a single row per ItemCode and datename(month, ad.applicationdate). Therefore, there is nothing to sum in your outer query since it is grouping by the same columns.
You also have the expression:
DateOfInspection = ''
which is highly suspicious based on the name of the column. What datatype is the DateOfInspection column? Doesn't sound like it should be string-based.
And lastly, the error message you posted sounds like it comes from SSRS and not sql server. Is that the case? Does your query run correctly from SSMS? Then the problem is in your report - and it would seem that you attempt to manipulate or interpret the Year column as a date (perhaps for sorting?). It also seems a bit short-sighted in your report design that your "Year" column is actually the name of a month and that your resultset does not include the year in some fashion. What happens when your data spans more than twelve months? And how do you intend to sort your report when you have month name but not month number?
I am attempting to write a query that pulls Order information from various tables. I have hit a road block at the target date value.
It seems that every time a target date is changed a new row is added in that table. All I want is to be able to select only the newest Target Date. What should I do?
select Distinct
OR01001 AS OrderNumber,
OR01002 AS OrderType,
OR01003 AS CustomerCode,
OR01015 AS OrderDate,
OR01017 AS CustomerREP,
OR01018 AS ContactPerson,
OR01019 AS SalesmanNumber,
OR03011 - OR03012 AS OpenQuantity,
SC03003 AS StockBalance,
OR01050 AS WarehouseNumber,
OR01072 AS CustomerPO,
OR03005 AS ItemCode,
OR03002 AS LineNumber,
OR500100.OR50004 As TargetDate
from OR010100
INNER Join OR030100 ON OR030100.OR03001 = OR010100.OR01001
INNER Join SL010100 ON SL010100.SL01001 = OR010100.OR01003
INNER Join SC030100 ON SC030100.SC03001 = OR030100.OR03005
Inner JOIN OR500100 ON OR500100.OR50001 = OR010100.OR01001
where OR010100.OR01002 <> 0 AND OR010100.OR01002 <> 6 AND OR01017 = 'SLOTT'
Order by OR01017 ASC;
If I understand your columns correctly, here's one way:
SELECT ...,
OR500100A.OR50004 AS TargetDate
FROM ...
INNER JOIN OR500100 AS OR500100A ON OR500100A.OR50001 = OR010100.OR01001
AND NOT EXISTS(SELECT 1 FROM OR500100 AS OR500100B
WHERE OR500100B.OR5001 = OR010100.OR01001
AND OR500100B.OR50004 > OR500100A.OR50004)
...
This makes sure you only get one OR500100 row with the latest value in OR50004 for the given OR5001.
from what i understand,
SELECT
...
MAX(OR500100.OR50004) As TargetDate
FROM...
WHERE...
GROUP BY --everything but OR500100.OR50004
ORDER BY...
should do the trick
EDIT: ty Ic.