Sql group by and join issue - sql

Perhaps just because I have had insufficient sleep this weekend, but I cannot seem to fix it.
I have these tables:
Products (PK Productid with fk categoryid)
ShippedProducts (PK ShippedProductsID with FK ShipmentID and fk productid)
Shipments (PK ShipmentID with FK InvoiceID)
Invoices (PK InvoiceID with FK CustomerID)
Customers (PK CustomerID with FK CountryId)
Countries (PK CountryID)
sorry, no other way to explain the schema. Please let me know if I can give a better overview for the data structure.
Here is my SQL (database is Microsoft SQL server 2008)
SELECT countries.countryid,
countryname,
isnull(round(sum(InvoiceTotal), 2),0) as TotalInvoice,
count(invoices.invoiceid) as nrOfInvoices,
count(shipments.shipmentid) as nrOfShipments
FROM INVOICES
inner join customers on invoices.CustomerID = customers.customerid
inner join countries on customers.CountryID = COUNTRIES.CountryID
inner join shipments on shipments.invoiceid = invoices.invoiceid
inner join ShippedProducts on ShippedProducts.ShipmentID = shipments.ShipmentID
group by countryname, COUNTRIES.CountryID, CurrencyName, CURRENCIES.CurrencyID
If I comment out the last inner join (with shippedproducts) I get the right number of invoices etc. but when I inner join the shippedproducts the count does not count the invoiceid but somehow the number of productrows from the shipment.
If I add more things to the group by, It does not group anymore by the country and I have a row for each invoice and shipment etc.
I somehow do not manage to see my mistake this monday. Perhaps I just need more coffee.

You should be able to use a windowing function to do this without a group by. Like this
SELECT
countries.countryid,
countryname,
isnull(round(sum(InvoiceTotal) over (partition by invoice.invoiceID), 2),0)
as TotalInvoice,
count(invoices.invoiceid)
over (partition by countries.countryid) as nrOfInvoicesPerCountry,
count(shipments.shipmentid) over (partition by coutries.countryid)
as nrOfShipments
FROM INVOICES
inner join customers on invoices.CustomerID = customers.customerid
inner join countries on customers.CountryID = COUNTRIES.CountryID
inner join shipments on shipments.invoiceid = invoices.invoiceid
inner join ShippedProducts on ShippedProducts.ShipmentID = shipments.ShipmentID

It appears your problem is joining on a 1 to many relationship.
I think you should either put your count of ShippedProducts.ShipmentProductsID into a subselect in your select statement. Something like this perhaps..
select countries.countryid,
countryname,
isnull(round(sum(InvoiceTotal), 2),0) as totalInvoice,
count(invoices.invoiceid) as nrOfInvoices,
count(shipments.shipmentid) as nrOfShipments
(select count shipmentproductsid from shipmentproducts where shipmentproducts.shipmentid = shipments.shipmentID) as totalProd
from invoices
inner join customers on invoices.customerid = customers.customerid
inner join countries on customers.countryid = countries.countryid
inner join shipments on shipments.invoiceid = invoices.invoiceid
group by countryname, countries.countryid , currencyname, currencies.currencyid

Related

SQL Server stored procedure query multiple tables

I am querying a SQL Server database using a stored procedure.
My database tables include:
Customers
SalesOrders - Linked to the customers with an id
SalesOrderLines - Linked to the SalesOrders with an id
SalesOrderReleases - Linked to the SalesOrderLines with an id, stores the quantity on the order line that has been released and ready to manufacture, the SalesOrderLine quantity can be all on one release or split up on multiple
FinishedGoods - linked to the SalesOrderLines with an id, stores the quantity of the SalesOrderLine where manufacturing is complete, the SalesOrderLine quantity can be all on one FinishedGood entry or split up on multiple
I need to retrieve all the customers that have SalesOrderLines with SalesOrderReleases and FinishedGoods where the total quantity finished is less than the total quantity released
I have tried this SQL code but Customers appear repeatedly in the results
SELECT
Customer.ID, Customer.Name
FROM
Customer
INNER JOIN
SalesOrder ON Customer.ID = SalesOrder.CustomerID
INNER JOIN
SalesOrderLine ON SalesOrder.ID = SalesOrderLine.SalesOrderID
INNER JOIN
SalesOrderRelease ON SalesOrderLine.ID = SalesOrderRelease.SalesOrderLineID
INNER JOIN
FinishedGood ON SalesOrderLine.ID = FinishedGood.SalesOrderLineID AND FinishedGood.Quantity < SalesOrderRelease.Quantity
I am looking for a SQL code snippet that will query multiple tables the way I have described.
try this code:
SELECT Customer.ID, Customer.Name FROM Customer
INNER JOIN SalesOrder ON Customer.ID = Order.CustomerID
INNER JOIN SalesOrderLine ON Order.ID = OrderLine.OrderID
INNER JOIN
(SELECT OrderID, OrderLineID, SUM (Quantity) AS SRQuantity FROM
SalesOrderRelease GROUP BY OrderID, OrderLineID) AS SRQ
ON SRQ.OrderID = SalesOrderLine.OrderID
INNER JOIN
(SELECT OrderLineID, SUM (Quantity) AS FGQuantity FROM
FinishedGoods GROUP BY OrderLineID) AS FGQ
ON SRQ.OrderLineID = FGQ.OrderLineID
WHERE FGQ.FgQuantity < SRQ.SRQuantity
Credits to Sergey for his answer, I was able to use the sample code he provided with several slight modifications:
SELECT Customer.ID, Customer.Name FROM Customer
INNER JOIN SalesOrder ON Customer.ID = SalesOrder.CustomerID
INNER JOIN SalesOrderLine ON SalesOrder.ID = SalesOrderLine.SalesOrderID
INNER JOIN
(SELECT SalesOrderID, SalesOrderLineID, SUM (Quantity) AS SRQuantity FROM
SalesOrderRelease GROUP BY SalesOrderID, SalesOrderLineID) AS SRQ
ON SRQ.SalesOrderLineID = SalesOrderLine.SalesOrderID
LEFT JOIN
(SELECT SalesOrderLineID, SUM (Quantity) AS FGQuantity FROM
FinishedGood GROUP BY SalesOrderLineID) AS FGQ
ON SRQ.SalesOrderLineID = FGQ.SalesOrderLineID
WHERE ISNULL(FGQ.FgQuantity, 0) < SRQ.SRQuantity
The last join needed to be a Left Join
When comparing the FgQuantity and the SRQuantity in the last line, I needed to have it check for NULL values
With these modifications everythings works perfectly!

SQL nested SELECT with JOIN

I wasted all the day on one query without success , SOS I need a help :) with a given #CustomerId , I need to query all the Products that linked to customer seller can sell but not sold to him before , the Commissions table is indication of what products seller can sell
Thanks in advance
SELECT sellableProduct
FROM (SELECT Comissions.ProductId AS sellableProduct, Sellers.SellerId AS sellableSeller FROM Comissions INNER JOIN Sellers ON Comissions.SellerId=Sellers.SellerId INNER JOIN Customers ON Sellers.SellerId=Customers.SellerId WHERE Customers.CustomerId = #customerid) AS tblSellable
LEFT JOIN (SELECT Sales.ProductId AS soldProduct, Customers.SellerId as soldSeller FROM Customers INNER JOIN Sales ON Customers.CustomerId=Sales.CustomerId WHERE Customers.CustomerId = #customerid) AS tblSold
ON tblSellable.sellableProduct=tblSold.soldProduct AND tblSellable.sellableSeller=tblSold.soldSeller
WHERE tblSold.soldProduct IS NULL
this way you avoid time-consuming IN statements
If a Customer can only have one Seller, then you can omit the seller link:
SELECT sellableProduct
FROM (SELECT Comissions.ProductId AS sellableProduct FROM Comissions INNER JOIN Sellers ON Comissions.SellerId=Sellers.SellerId INNER JOIN Customers ON Sellers.SellerId=Customers.SellerId WHERE Customers.CustomerId = #customerid) AS tblSellable
LEFT JOIN (SELECT Sales.ProductId AS soldProduct FROM Sales WHERE Sales.CustomerId = #customerid) AS tblSold
ON tblSellable.sellableProduct=tblSold.soldProduct
WHERE tblSold.soldProduct IS NULL
Basically, you're looking for products that have a record in commissions, but not in sales. Using :id to denote the specific ID:
SELECT *
FROM products
WHERE productid IN (SELECT productid
FROM commissions
WHERE sellerid = :id) AND
productid NOT IN (SELECT productid
FROM sales
JOIN customers ON sales.customerid = cusomers.customerid
WHERE sellerid = :id)
Would this work?
SELECT sell.*, prod.* FROM
Sellers sell
INNER JOIN Customers cust ON cust.SellerId = sell.SellerId
LEFT JOIN Commissions comm ON sell.SellerId = comm.SellerId
LEFT JOIN Products prod ON prod.ProductId = comm.ProductId
WHERE prod.ProductId NOT IN (
SELECT ProductId
FROM Products p INNER JOIN
Sales s ON s.ProductId = p.ProductId
WHERE s.CustomerId = #CustomerId
It get all the sellers and the respective product from commission, where the product Id is NOT associated with any sale of the client

Insert Multiple Tables into one Table

Http://i.stack.imgur.com/z0fSP.png
Http://i.stack.imgur.com/Nd3nB.png
Http://i.stack.imgur.com/5qtRh.png
Http://i.stack.imgur.com/01PVc.png
Http://i.stack.imgur.com/wvibY.png
INSERT INTO OrderArchive
(OrderNumber,OrderDate,StockItem,Quantity,UnitPrice,SalesRep,Customer,ArchiveDate)
SELECT "ORDER".OrderNo,
"ORDER".OrderDate,
Stock.StockNo||' '||Stock.StockDesc,
Orderline.Quantity,
Orderline.UnitPrice,
Person.FirstName||' '||Person.Surname,
Person.FirstName||' '||Person.Surname,
OrderArchive.ArchiveDate
FROM "ORDER"
INNER JOIN Stock
ON Stock.StockNo||' '||Stock.StockDesc = OrderAchive.StockItem
INNER JOIN Orderline
ON Orderline.Quantity = OrderArchive.Quantity
INNER JOIN Orderline
ON Orderline.UnitPrice = OrderArchive.UnitPrice
INNER JOIN Person
ON Person.FirstName||' '||Person.Surname = OrderArchive.SalesRep
INNER JOIN Person
ON Person.FirstName||' '||Person.Surname = OrderArchive.Customer
WHERE "ORDER".OrderDate < DATEADD('M',6,SYSDATE);
I tried to work it out first, but just couldnt manage it .... i am trying to move orders that are older than 6 months to OrderArchive...OrderArchive has OrderNo & Orderdate that come from "ORDER"Table Stock item which is a combination of StockID and Stockdesc from stock table quantity and unit cost are from orderline table salesrep and customer are from person Table and datearchived is a sysdate of the date the order was archived
To do this, you need a way to join the Person record to the TableOrder record. This code assumes you have a PersonID on the TableOrder table. You might have a CustomerID or SalesPersonID or something like that on your TableOrder table to join it to the Person table, but I'm not clear on that.
INSERT INTO ArchiveTable (col_1,col_2,col_3,col_4 etc)
SELECT
o.OrderNo,
o.OrderDate,
p.StockNo||' '||p.StockDesc,
p.FirstName||' '|| p.Surname
FROM TableOrder o
INNER JOIN Person p
ON p.PersonID = o.PersonID
WHERE o.orderdate < DATEADD('M',12,SYSDATE)
I think that you are not getting the expected output from the query, because you are joining the Person table with both SalesRep and Customer names at the same time. Thus, only if an Order has the same name for both SalesRep and Customer, will you get any data. Therefore, I have created different table aliases for the Person table, as below:
INSERT INTO OrderArchive
(OrderNumber,OrderDate,StockItem,Quantity,UnitPrice,SalesRep,Customer,ArchiveDate)
SELECT
"ORDER".OrderNo,
"ORDER".OrderDate,
Stock.StockNo||' '||Stock.StockDesc,
Orderline.Quantity,
Orderline.UnitPrice,
Person_SR.FirstName||' '||Person_SR.Surname,
Person_C.FirstName||' '||Person_C.Surname,
OrderArchive.ArchiveDate
FROM "ORDER"
INNER JOIN Stock
ON Stock.StockNo||' '||Stock.StockDesc = OrderAchive.StockItem
INNER JOIN Orderline
ON Orderline.Quantity = OrderArchive.Quantity
INNER JOIN Orderline
ON Orderline.UnitPrice = OrderArchive.UnitPrice
INNER JOIN Person Person_SR
ON Person_SR.FirstName||' '||Person_SR.Surname = OrderArchive.SalesRep
INNER JOIN Person Person_C
ON Person_C.FirstName||' '||Person_C.Surname = OrderArchive.Customer
WHERE "ORDER".OrderDate < DATEADD('M',6,SYSDATE);
INSERT INTO OrderArchive (OrderNumber,OrderDate,StockItem,Quantity,UnitPrice,SalesRep,Customer)
SELECT "ORDER".OrderNo,
"ORDER".OrderDate,
Stock.StockNo ||' '|| Stock.StockDesc,
Orderline.Quantity,
Stock.UnitCost,
"ORDER".SalesRepID,
Person.FirstName||' '||Person.Surname
FROM Person INNER JOIN
("ORDER" LEFT JOIN
(Orderline INNER JOIN Stock
ON Orderline.StockID=Stock.StockNo)
ON"ORDER".OrderNo=OrderLine.OrderNo)
ON Person.PersonID="ORDER".CustomerID
WHERE "ORDER".OrderDate < ADD_MONTHS (SYSDATE,-6)
ORDER BY "ORDER".OrderNo;
SELECT * FROM OrderArchive;
Nearly Done i just have to get SalesRepID to say firstname surname of salesrep but mostly done :)

SQL inner join not returning records with blank values

I have 2 different types of records in my 'items' table (postgreSQL database). Some items have invoiceid associated, which has customer information associated. The other items in my items table do not have an invoice number associated.
I am trying to return a list of items with invoice date and customer names. The items that don't have invoice or customerer associated will also show, but those fields will just be blank. The problem is with my current sql statment. It only shows the items with invoice info associated.
select items.ItemID, items.qty, items.description, customers.firstname,
customers.lastname, invoices.InvoiceDate, items.status
from items
inner join Invoices on items.InvoiceID = Invoices.InvoiceID
inner join customers on Invoices.CustomerID = Customers.CustomerID
where Items.Status = 'ONTIME'
ORDER BY InvoiceDate asc
Any ideas how I can get all records to show, or is it even possible? The fields that don't have data are NULL, i'm not sure if that is part of the problem or not.
You want to use left outer join instead of inner join:
select i.ItemID, i.qty, i.description, c.firstname,
c.lastname, inv.InvoiceDate, i.status
from items i left outer join
Invoices inv
on i.InvoiceID = inv.InvoiceID left outer join
customers c
on inv.CustomerID = c.CustomerID
where i.Status = 'ONTIME'
order by InvoiceDate asc;
I also introduced table aliases to make the query a bit easier to read.

Using DISTINCT in SQL Query

How do i use DISTINCT command in a SQL query to show the supplier id, company name, and the number of distinct products from that supplier that were ordered prior to a specific date? I ran the code in Access but it doesn't translate over to SQL efficiently. The table appears.
[Supplier ID Company Name Product Name Order Date
1 Exotic Liquids Chang 17-Aug-94
1 Exotic Liquids Chang 22-Nov-94
1 Exotic Liquids Aniseed Syrup 26-Sep-94]
The code I have so far is the following. Where I get confused is where to put the DISTINCT statement. Should it be immediately after the Select? Should it go in Parentheses in addition to the SELECT? Excuse my lack of knowledge on this subject in advance.
SELECT Suppliers.SupplierID, Customers.CompanyName, Products.ProductName,
Orders.OrderDate
FROM Suppliers INNER JOIN
Products ON Suppliers.SupplierID = Products.SupplierID CROSS JOIN
Customers INNER JOIN
Orders ON Customers.CustomerID = Orders.CustomerID
WHERE Orders.OrderDate <='1/1/1999'
ORDER BY Suppliers.SupplierID
You can either distinct by all columns selected :
SELECT DISTINCT
Suppliers.SupplierID, Customers.CompanyName, Products.ProductName,
Orders.OrderDate
FROM
Suppliers INNER JOIN
Products ON Suppliers.SupplierID = Products.SupplierID CROSS JOIN
Customers INNER JOIN
Orders ON Customers.CustomerID = Orders.CustomerID
WHERE
Orders.OrderDate <='1/1/1999'
ORDER BY
Suppliers.SupplierID
or use group by instead if you need to distinct only by SupplierID. DISTINCT is not a function, hence DISTINCT(Suppliers.SupplierID) means the same as simply put DISTINCT word after SELECT in this case (see the 2nd reference below).
For Reference :
http://blog.sqlauthority.com/2007/12/20/sql-server-distinct-keyword-usage-and-common-discussion/
http://weblogs.sqlteam.com/jeffs/archive/2007/10/12/sql-distinct-group-by.aspx
I'm pretty sure it's this:
SELECT DISTINCT(Suppliers.SupplierID), Customers.CompanyName, Products.ProductName,Orders.OrderDate
FROM Suppliers INNER JOIN
Products ON Suppliers.SupplierID = Products.SupplierID CROSS JOIN
Customers INNER JOIN
Orders ON Customers.CustomerID = Orders.CustomerID
WHERE Orders.OrderDate <='1/1/1999'
ORDER BY Suppliers.SupplierID