Group nested selected query returning all rows - sql-server

I'm trying to display the data so that only one line is displayed per customer, i'm having trouble with trying to achieve that with my code as its returning all records, can anyone help
SELECT customerOrdrs.NAME AS 'Name',
customerOrdrs.currentbalance -
Sum(COALESCE(customerOrdrs.revisedbalance, 0)) AS 'RevisedBalance',
sold AS 'NumberOfItemsSold'
FROM customers,
(SELECT c.NAME AS NAME,
c.balance AS CurrentBalance,
i.qty AS RevisedBalance,
( Min(s.price) * i.qty ) AS Sold
FROM customers c
INNER JOIN sales o
ON c.NAME = o.custname
INNER JOIN purchases i
ON i.orderno = o.orderno
INNER JOIN contracters s
ON i.item = s.item
GROUP BY c.NAME,
c.balance,
i.qty) customerOrdrs
GROUP BY customerOrdrs.NAME,
customerOrdrs.currentbalance,
sold

I'm not sure how your data looks but I have reformatted the query and there are a few things I've noticed off the bat.
I have removed the subquery as I don't believe it is necessary - in addition your original query is referring to customer table twice without defining a join
Select [C].[Name] As [Name]
, [CurrentBalance] = [C].[Balance]
, [RevisedBalance] = [C].[Balance] - Sum([P].[Qty])
, [Sold] = ( Min([CO].[Price]) * sum([P].[Qty]) )
From [CUSTOMERS] [C]
Inner Join [Sales] [s]
On [C].[Name] = [s].[custName]
Inner Join [Purchases] [P]
On [P].[OrderNo] = [s].[OrderNo]
Inner Join [Contracters] [CO]
On [P].[Item] = [CO].[Item]
Group By [C].[Name]
, [C].[Balance];

Related

Avoid select data from other tables

I'm coding a movies web app, for showing some tags I'm trying to write a query that returns the movie name and the count of how many categorys has assigned it. I'm trying to add a filter that for example: if X movie contains a "Comedy" category this movie doesn't even need to be consider in my query.
At the moment this is the query that I have:
SELECT A.*, B.*, C.* -- A.name, count(C.name) [Categories]
FROM movies A
INNER JOIN moviesGenres B ON A.id = B.movieId
INNER JOIN genres C ON B.genreId = C.id
WHERE C.name <> 'Comedy'
-- group by A.name, C.name
-- having count(C.name) > 2
At the moment this query is working to return the expected output. But if you run that query with the data in this SQL Fiddle you'll see that is considering the movie "Bad Boys" but this movie has assigned one "Comedy" category so any data from this movie should't be considered.
You need to put that condition in the having clause wich is the where clause of a group.
SELECT m.name, count(g.name) [Genres]
FROM movies m
INNER JOIN moviesGenres mg ON m.id = mg.movieId
INNER JOIN genres g ON mg.genreId = g.id
GROUP BY m.name
HAVING sum(case when g.name = 'Comedy' then 1 else 0 end) = 0
If you really want to select columns from [moviesGenres] and [genres], go for juergen's answer. If you don't need them, anti join them:
SELECT A.*, B.*, C.* -- A.name, count(C.name) [Categories]
FROM
movies A
WHERE NOT EXISTS
(
SELECT 1
FROM
moviesGenres Bneg
inner join genres Cneg ON Bneg.genreId = Cneg.id
WHERE
A.id = Bneg.movieId
and Cneg.name = 'Comedy'
)

Multiple Nested Inner Joins: not all records are shown

I have difficulty joining two tables that look like the following:
The main table PMEOBJECT which has a unique key named OBJECTID and
has in total 12768 rows.
Then I want to join PMEOBJECTVALIDITY on it which has an n:1 relationship with PMEOBJECT, since it has more rows,
because it saves the changes over time of PMEOBJECT (i.e. when a certain object is not
valid anymore), this one has 12789 rows (meaning only 21 objects
changed over time). However, I only want to have the current last
VALIDFROM date shown in the query. This all works fine.
Then the trouble starts when I want to join PMEOBJECTDIMENSION, which has an
n:1 relationship with PMEOBJECTVALIDITY and has 36737 rows in total.
SELECT
PMEOBJECT.OBJECTID
,PMEOBJECTVALIDITY.VALIDFROM
,PMEOBJECTDIMENSION.DIMENSION2_
FROM PMEOBJECT
LEFT JOIN PMEOBJECTVALIDITY
ON PMEOBJECTVALIDITY.OBJECTID = PMEOBJECT.OBJECTID
AND PMEOBJECTVALIDITY.DATAAREAID = PMEOBJECT.DATAAREAID
INNER JOIN(
SELECT
OBJECTID,
MAX(VALIDFROM) AS NEWFROMDATE,
MAX(VALIDTO) AS NEWTODATE
FROM PMEOBJECTVALIDITY B
GROUP BY OBJECTID
) B
ON PMEOBJECTVALIDITY.OBJECTID = B.OBJECTID
AND PMEOBJECTVALIDITY.VALIDFROM = B.NEWFROMDATE
LEFT JOIN PMEOBJECTDIMENSION
ON PMEOBJECTDIMENSION.OBJECTVALIDITYID = PMEOBJECTVALIDITY.RECID
AND PMEOBJECTDIMENSION.DATAAREAID = PMEOBJECTVALIDITY.DATAAREAID
INNER JOIN(
SELECT
OBJECTVALIDITYID,
MAX(VALIDFROM) AS NEWFROMDATE_2
FROM PMEOBJECTDIMENSION C
GROUP BY OBJECTVALIDITYID
) C
ON PMEOBJECTDIMENSION.OBJECTVALIDITYID = C.OBJECTVALIDITYID
AND PMEOBJECTDIMENSION.VALIDFROM = C.NEWFROMDATE_2
Results in query per step:
SELECT PMEOBJECT: 12768 rows
LEFT JOIN PMEVALIDITY: 12789 rows
INNER JOIN PMEVALIDITY: 12768 rows
LEFT JOIN PMEOBJECTDIMENSION: 36737 rows
INNER JOIN PMEOBJECTDIMENSION: 12729 rows
I want the end result again to have the same 12768 rows, I don't want any ObjectId to be left out.
What am I missing here?
Kind regards,
Igor
Following might help:
from PMEOBJECTDIMENSION onwards:
LEFT JOIN (SELECT PMEOBJECTDIMENSION.OBJECTVALIDITYID, PMEOBJECTDIMENSION.DATAAREAID
FROM PMEOBJECTDIMENSION
INNER JOIN(SELECT OBJECTVALIDITYID, MAX(VALIDFROM) AS NEWFROMDATE_2
FROM PMEOBJECTDIMENSION C
GROUP BY OBJECTVALIDITYID
) C
ON PMEOBJECTDIMENSION.OBJECTVALIDITYID = C.OBJECTVALIDITYID
AND PMEOBJECTDIMENSION.VALIDFROM = C.NEWFROMDATE_2
)X
ON X.OBJECTVALIDITYID = PMEOBJECTVALIDITY.RECID
AND X.DATAAREAID = PMEOBJECTVALIDITY.DATAAREAID
and select the distinct records if duplicates present.
The INNER JOINs are filtering out records- what you want is that the LEFT JOIN table (PMEOBJECTVALIDITY and PMEOBJECTDIMENSION) should only include records that have at least a match on the INNER JOIN queries (alias B and C). You can accomplish this with by nesting the INNER JOIN with the LEFT JOIN, generally done as follows:
SELECT *
FROM A
LEFT JOIN B
INNER JOIN C
ON B.ID = C.BID
ON A.ID = B.AID
Now B is INNER JOINed on C and will only contain records that have a match in C, but will preserve the LEFT JOIN not remove any records from A.
In your case, you can simply move the ON clause from the LEFT JOIN to the end of the following INNER JOIN.
SELECT
PMEOBJECT.OBJECTID
,PMEOBJECTVALIDITY.VALIDFROM
,PMEOBJECTDIMENSION.DIMENSION2_
FROM PMEOBJECT
LEFT JOIN PMEOBJECTVALIDITY
INNER JOIN(
SELECT
OBJECTID,
MAX(VALIDFROM) AS NEWFROMDATE,
MAX(VALIDTO) AS NEWTODATE
FROM PMEOBJECTVALIDITY B
GROUP BY OBJECTID
) B
ON PMEOBJECTVALIDITY.OBJECTID = B.OBJECTID
AND PMEOBJECTVALIDITY.VALIDFROM = B.NEWFROMDATE
ON PMEOBJECTVALIDITY.OBJECTID = PMEOBJECT.OBJECTID
AND PMEOBJECTVALIDITY.DATAAREAID = PMEOBJECT.DATAAREAID --here it is!
LEFT JOIN PMEOBJECTDIMENSION
INNER JOIN(
SELECT
OBJECTVALIDITYID,
MAX(VALIDFROM) AS NEWFROMDATE_2
FROM PMEOBJECTDIMENSION C
GROUP BY OBJECTVALIDITYID
) C
ON PMEOBJECTDIMENSION.OBJECTVALIDITYID = C.OBJECTVALIDITYID
AND PMEOBJECTDIMENSION.VALIDFROM = C.NEWFROMDATE_2
ON PMEOBJECTDIMENSION.OBJECTVALIDITYID = PMEOBJECTVALIDITY.RECID
AND PMEOBJECTDIMENSION.DATAAREAID = PMEOBJECTVALIDITY.DATAAREAID --I'm here

SQLServer reference outer query

Is it possible to reference an outer query from an inner query in the context of a join? The "where (sid.ItemID = i.itemID)" of the inner query is giving me an error. I thought I did this in the past so I search through all my stored procs but apparently I did some kind of substitution to get it to work. I suspect I can delete that line and it will work but is it more effecient with that inner where clause?
SELECT departmentName
, supplierName
, so.SalesOrderID
, ss.warehouseInvoiceNo
, ss.transactionNo
, ss.storeID
, s.storeName
, s.storeNo
, tr.transactionDate
, p.period
, sooos.salesOrderID
, sooos.salesOrderOutOfStockID
, sooos.itemID
, i.itemNo
, i.itemName
, i.pack
, i.unitSize
, quantity
, wi.available
FROM SalesOrderOutOfStock sooos
JOIN Item AS i ON i.ItemID = sooos.ItemID
JOIN SalesOrder so ON so.SalesOrderID = sooos.SalesOrderID
JOIN WarehouseInventory wi ON wi.ItemID = sooos.ItemID
JOIN Store s ON s.StoreID = so.StoreID
JOIN InvoiceOrderRelationship ior ON ior.SalesOrderID = so.SalesOrderID
JOIN StockSale ss ON ss.WarehouseInvoiceNo = ior.WarehouseInvoiceNo
JOIN TransactionRegister tr ON tr.TransactionNo = ss.TransactionNo
JOIN Period p ON p.PeriodID = tr.PeriodID
JOIN Department d ON d.DepartmentID = i.DepartmentID
LEFT OUTER JOIN (SELECT TOP 1 itemID
, supplierID
FROM SupplierInvoiceDetail sid
JOIN SupplierInvoice si ON si.SupplierInvoiceID = sid.SupplierInvoiceID
--where (sid.ItemID = i.itemID)
order by InvoiceDate desc
--NEED AN ORDER BY HERE
) AS lastSupplier ON lastSupplier.ItemID = i.ItemID
JOIN supplier su ON su.SupplierID = Isnull(lastSupplier.supplierID, i.supplierID)
WHERE ss.WarehouseInvoiceNo = 10000000
--$P{invoiceNo}
You need to use OUTER APPLY here rather than LEFT JOIN:
OUTER APPLY (SELECT TOP 1 itemID
, supplierID
FROM SupplierInvoiceDetail sid
JOIN SupplierInvoice si ON si.SupplierInvoiceID = sid.SupplierInvoiceID
where (sid.ItemID = i.itemID) order by InvoiceDate desc
) AS lastSupplier
Subqueries introduced in FROM or JOIN clauses cannot refer to other table sources within the same FROM clause (effectively, they should all be evaluatable simultaneously). APPLY allows you to introduce a specific dependency in the evaluation.

paging and ordering a MS Access query

i have the following MS ACCESS query that i would like it to return results ordered by name and "paged" by "faking" a rownumber
select * from (SELECT *
FROM (SELECT
s.name as SHolderCategory,
c1.id,
c1.fmember,
c1.link,
m.name as category,
c1.name,
c1.address1,
c1.address2,
c1.city,
c1.state,
c1.zip,
(SELECT COUNT(c2.id) FROM orgs AS c2 WHERE c2.id <= c1.id) AS rownumber
FROM
((orgs AS c1 inner join membershipcls m on m.Id = c1.mClassID)
inner join SHolderscategories s on s.Id = c1.SHolderCategoryID
)
where c1.active = 1)
order by c1.name)
WHERE rownumber > 20 AND rownumber <=40
the problem here is that the ordering is done before the where clause which enforces paging.
so it ends up sorting one page at a time, rather than sorting the whole resultset then paging it...so the results are wrong because in page 1 i have names starting with a to g ... then in page 2 it comes back to names starting with c .... and so on
when i try to get the order clause out so that the query executes the paging first...Mr ACCESS is Angry!!! and tells me it is a COMPLEX query !!!!
any workaround for this?
try also this approach:
SELECT * FROM
(
SELECT TOP 20 *
FROM
(
SELECT TOP 40
s.name as SHolderCategory,
c1.id,
c1.fmember,
c1.link,
m.name as category,
c1.name,
c1.address1,
c1.address2,
c1.city,
c1.state,
c1.zip
FROM
orgs AS c1
inner join membershipcls m on m.Id = c1.mClassID
inner join SHolderscategories s on s.Id = c1.SHolderCategoryID
WHERE c1.active = 1
ORDER BY c1.name
) o
ORDER BY o.name DESC
) f ORDER BY f.name

Get distinct rows when filtering and using dynamic order by

My first version of the question was confusing, I need to make smaller chunks.
If a user can filter products from a website, one product should occur only once in the list.
Because of joins this code gives me two same products, how do I solve that?
I think I need a solution without using distinct because it will give me headache later on.
code from AW2012:
declare #safetystocklevel int
set #safetystocklevel = 1000
declare #status int
set #status = 2
select * from Production.Product p
inner join Purchasing.ProductVendor pv on p.ProductID = pv.ProductID
inner join Purchasing.Vendor v on v.BusinessEntityID = pv.BusinessEntityID
inner join Production.ProductDocument pd on p.ProductID = pd.ProductID
inner join Production.Document d on d.DocumentNode = pd.DocumentNode
WHERE
(#safetystocklevel = '' or p.SafetyStockLevel = #safetystocklevel)
and (#status = '' or d.Status = #status)
output:
ProductId Name
506 Reflector
506 Reflector
Edit:
Thanks, I now use Group by to get distinct rows.
Yeah, maybe using group by works for me, Im gonna do some testing now.....
Hi again
I want all products to be searchable, so I guess I need left outer joins to achieve that.
When I add dynamic order by I get into trouble, more rows are added.
Probably because I must add poh.Status to the group by.
There are 504 rows in the product table, this query returns 776 rows.
(I have removed the filtering in WHERE since it is not interesting now, and Im joining to other tables now just to get more rows to play with)
Code:
declare #sortType nvarchar(50)
set #sortType = 'Status'
select p.ProductID,
CASE WHEN #sortType = 'Status' THEN poh.Status END as Status,
CASE WHEN #sortType = 'ProductId' THEN p.ProductID END as ProductId
from Production.Product p
left outer join Purchasing.PurchaseOrderDetail pod on p.ProductID = pod.ProductID
left outer join Purchasing.PurchaseOrderHeader poh on poh.PurchaseOrderID = pod.PurchaseOrderID
left outer join Production.ProductDocument ppd on ppd.ProductID = p.ProductID
left outer join Production.Document pd on pd.DocumentNode = ppd.DocumentNode
group by p.ProductID, poh.Status
ORDER BY
CASE WHEN #sortType = 'Status' THEN poh.Status END ASC,
CASE WHEN #sortType = 'ProductId' THEN p.ProductID END ASC
You can use Group By ProductId, Name, to select the single row, if you are not planning to include distinct. But I'll prefer "distinct" if you are not using any aggregate value in select clause.
select p.ProductId, p.Name from Production.Product p
inner join Purchasing.ProductVendor pv on p.ProductID = pv.ProductID
inner join Purchasing.Vendor v on v.BusinessEntityID = pv.BusinessEntityID
inner join Production.ProductDocument pd on p.ProductID = pd.ProductID
inner join Production.Document d on d.DocumentNode = pd.DocumentNode
WHERE
(#safetystocklevel = '' or p.SafetyStockLevel = #safetystocklevel)
and (#status = '' or d.Status = #status)
GROUP BY p.ProductId, p.Name

Resources