How to Count total rows on a shipment - sql

I'm trying to count the total number of lines on each shipment:
SELECT Shipments.ShipmentId,
SalesOrders.SalesOrderId as OrderNumber,
Count(SalesOrderItems.SalesOrderItem) as NumberOfLines
FROM SalesOrders
INNER JOIN SalesOrderItems on SalesOrders.SalesOrder = SalesOrderItems.SalesOrder
INNER JOIN Shipments on SalesOrderItems.SalesOrder = Shipments.SalesOrder
GROUP BY SalesOrderItems.SalesOrderItem, SalesOrders.SalesOrderId, Shipments.ShipmentId
ORDER BY Shipments.ShipmentID ASC
Currently I'm getting:
ShipmentID | OrderNumber | NumberOfLines
SH00000001 | SO-0000001 | 1
SH00000001 | SO-0000001 | 1
SH00000002 | SO-0000007 | 1
SH00000003 | SO-0000006 | 1
SH00000003 | SO-0000006 | 1
And I should be getting:
ShipmentID | OrderNumber | NumberOfLines
SH00000001 | SO-0000001 | 1
SH00000001 | SO-0000001 | 2
SH00000002 | SO-0000007 | 1
SH00000003 | SO-0000006 | 1
SH00000003 | SO-0000006 | 2

Remove SalesOrderItems.SalesOrderItem from your group by clause, you don't want it (as can be deduced from it not existing on your sample result dataset).

Your GROUP BY clause should match the unaggregated columns in the SELECT:
SELECT s.ShipmentId,
so.SalesOrderId as OrderNumber,
Count(soi.SalesOrderItem) as NumberOfLines
FROM SalesOrders so INNER JOIN
SalesOrderItems soi
ON so.SalesOrder = soi.SalesOrder INNER JOIN
Shipments s
ON soi.SalesOrder = s.SalesOrder
GROUP BY soi.SalesOrderId, s.ShipmentId
ORDER BY s.ShipmentID ASC;
Note that I've added table aliases. These make the queries easier to write and to read.

Related

Get related tables not contains result

I have a DesignGroup table as:
+--------------------------------------+----------+
| DesignGroupId | Name |
+--------------------------------------+----------+
| 3A81C1FF-442F-4291-B8E2-7079D80920CF | Design 1 |
| 3238F4C6-7BA7-4B3F-9383-17702B0D1CC3 | Design 2 |
+--------------------------------------+----------+
Each DesignGroup can have multiple customers, so I have a table DesignGroupCustomers as:
+--------------------------------------+--------------------------------------+-------------+
| DesignGroupCustomerId | DesignGroupId (FK) | CustomerKey |
+--------------------------------------+--------------------------------------+-------------+
| D0828677-F295-46F7-BB85-65888D5A48B7 | 3A81C1FF-442F-4291-B8E2-7079D80920CF | 10 |
| 10C01BB9-1DDB-4DB4-BEC4-9539E030BF68 | 3A81C1FF-442F-4291-B8E2-7079D80920CF | 20 |
| F88C9F66-C0D9-EB11-8481-5CF9DDF6DC87 | 3238F4C6-7BA7-4B3F-9383-17702B0D1CC3 | 10 |
+--------------------------------------+--------------------------------------+-------------+
Each customer have a CustomerType as, customerTable:
+-------------+-------------+
| CustomerKey | CustTypeKey |
+-------------+-------------+
| 10 | 2 |
| 20 | 1 |
+-------------+-------------+
That I want to achieve is to get only this statement:
return only the DesignGroup who not have a customer with custTypeKey = 1
In this case it should return Design 2 because it does not have customer with custTypeKey = 1
I was thinking about CTE usage but I just have not idea how to get the desire result:
;WITH CTE
AS (SELECT
[DG].[DesignGroupId]
, ROW_NUMBER() OVER(PARTITION BY [DesignGroupCustomer]) AS [RN]
FROM [DesignGroup] AS [DG]
INNER JOIN [DesignGroupCustomer] AS [DGC] ON [DG].[DesignGroupId] = [DGC].[DesignGroupId]
INNER JOIN [Customer] AS [C] ON [DGC].[CustomerKey] = [C].[CustomerKey]
INNER JOIN [CustomerType] AS [CT] ON [C].[CustTypeKey] = [CT].[CustTypeKey])
SELECT
[DesignGroupId]
FROM [CTE] -- WHERE CustomerType NOT CONTAINS (1)
WITH temp AS (
SELECT DISTINCT
dgc.DesignGroupId AS DesignGroupId
FROM DesignGroupCustomers dgc
INNER JOIN customerTable ct
ON dgc.CustomerKey = ct.CustomerKey
WHERE ct.CustTypeKey = 1
)
SELECT
DesignGroupId
FROM DesignGroup
WHERE DesignGroupId NOT IN (
SELECT
DesignGroupId
FROM temp
)
Firstly, you can get all designgroups having CustTypeKey =1 and then get all other designgroups using NOT IN. Please let me know if you face any issues
You can use a subquery to return the design groups which have this customer type key of 1 and then LEFT JOIN the subquery on the design table and filter down to results that have a DesignGroupId of null (any design group that isn't included in the dataset of the subquery)
SELECT d.[DesignGroupId]
FROM [DesignGroup] AS d
LEFT JOIN
(
SELECT dgc.[DesignGroupId]
FROM [DesignGroupCustomer] AS dgc
ON dgc.[DesignGroupId] = d.[DesignGroupId]
INNER JOIN [Customer] AS c
ON c.[CustomerKey] = dgc.[CustomerKey]
WHERE c.[CustTypeKey] = 1
GROUP BY dgc.[DesignGroupId]
) x
ON x.[DesignGroupId] = d.[DesignGroupId]
WHERE x.[DesignGroupId] IS NULL

Linking tables on multiple criteria

I've got myself in a bit of a mess on something I'm doing where I'm trying to get two tables linked together based on multiple bits of info.
I want to link one table to another based on the basic rules of(in this hierarchy)
where main linking is where orderid matches between the two tables
records from table 2 where valid=Y,
from those i want the valid records which has the highest seqn1 number and then from those the one that has the highest seqn2 value
table1
orderid | date | otherinfo
223344 | 22/10/2020 | okokkokokooeodijjf
table2
orderid | seqn1 | seqn2 | valid | additonaldata
223344 | 1 | 3 | y | sdfsfsf
223344 | 2 | 1 | y | sffferfr
223344 | 2 | 2 | y | sfrfrefr -- This row
223344 | 2 | 3 | n | rfrg66rr
223344 | 2 | 4 | n | adwere
223344 | 3 | 4 | n | adwere
so would want the final record to be
orderid | date | otherinfo | seqn1 | seqn2 | valid | additonaldata
223344 | 22/10/2020 | okokkokokooeodijjf | 2 | 2 | y | sfrfrefr
I started off with the code below but I'm not sure I'm doing it right and I can't seem to get it to pay attention to the valid flag when i try to add it in.
SELECT * FROM table1
left JOIN table2
ON table1.orderid = table2.orderid
AND table2.seqn1 = (SELECT MAX(table2.seqn1) FROM table2 WHERE table1.orderid = table2.orderid)
AND table2.seqn2 = (SELECT MAX(table2.seqn2) FROM table2 WHERE table1.orderid = table2.orderid
AND table2.seqn1 = (SELECT MAX(table2.seqn1) FROM table2 WHERE table1.orderid = table2.orderid))
Could someone help me amend the code please.
Use row_number analytic function with partition by orderid and order by SEQNRs in the order you need. No need for multiple subselects. To add more selections for the single row, use CASE to map your values to numbers and order by them also.
Fiddle here.
with l as (
select *,
rank() over(partition by orderid order by seqn1 desc, seqn2 desc) as rn
from line
where valid = 'y'
)
select *
from header as h
join l
on h.orderid = l.orderid
and l.rn = 1
How about something like this:
;
with cte_table2 as
(
SELECT ordered
,MAX(seqn1) as seqn1
,MAX(seqn2) as seqn2
FROM table2
where valid = 'y'
group by ordered --check if you need to add 'valid' to the group by but I don't think so.
)
SELECT
t1.*
,t3.otherinfo
--,t3.[OtherFields]
from table1 t1
inner join cte_table2 t2 on t1.orderid = t2.orderid -- first match on id
left join table2 t3 on t3.orderid = t2.orderid and t3.seqn1 = t2.seqn1 and t3.seqn2 = t2.seqn2

In a PostgreSQL query how to filter results from a field in a join

My table structure is as follows:
Restaurants
| id | name |
|----|---------|
| 1 | The Hut |
| 2 | T Burger|
Dishes:
| id | name |
|----|---------|
| 1 | Pizza |
| 2 | Caramel |
Orders:
| id | locatio |
|----|---------|
| 1 | New York|
| 2 | London |
_RestaurantDishes:
In here, B represents restaurants and A represents dishes.
A restaurant can have many dishes. A dish can only have one restaurant.
| id | A | B |
|----|---|---|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
_DishOrders:
In here, B represents orders and A represents dishes.
A dish can have many orders. An order can have many dishes.
| id | A | B |
|----|---|---|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
What I want to do is, get a list of dishes from a selected list of restaurants and sort them according to the orders count. I tried to do it like this:
SELECT count(dishOrder.id) as myCount, dish.id, name
FROM "default$default"."Dish" dish
left join "default$default"."_RestaurantDishes" dishRestaurant on dish.id = dishRestaurant."A"
left join "default$default"."_DishOrders" dishOrder on dish.id = dishorder."A"
where "dishRestaurant"."B" in ("1", "2")
group by dish.id order by mycount desc;
But it gives me the error ERROR: missing FROM-clause entry for table "dishRestaurant". I tried many other approaches but didn't work.
you are getting error ERROR: missing FROM-clause entry for table "dishRestaurant" because
you used double quote in number in ("1", "2") which turned into column name for using double quote. you have to change it like in (1,2)
SELECT count(dishOrder.id) as myCount, dish.id, name
FROM "default$default"."Dish" dish
left join "default$default"."_RestaurantDishes" dishRestaurant on dish.id = dishRestaurant."A"
left join "default$default"."_DishOrders" dishOrder on dish.id = dishorder."A"
where "dishRestaurant"."B" in (1, 2)
group by dish.id ,name
order by myCount desc;
you also used dish.id, name in selection but not used name in group by, so you have to use that in group by
Don't escape identifiers if you can avoid it. The escaping fixes the casing -- and strange things happen.
I suspect that you want:
select count(*) as myCount, d.id, d.name
from "default$default"."Dish" d left join
"default$default"."_RestaurantDishes" dr
on dr."A" = d.id and
dr."B" in (1, 2) left join
"default$default"."_DishOrders" do
on d.id = do."A"
group by d.id, d.name
order by mycount desc;
Notes:
I simplified the table aliases and removed the double quotes from the aliases defined in the query.
You should remove the double quotes from the column names -- if you can.
I added d.name back into the group by. This is not needed if d.id is unique.
I use count(*), so no columns are referenced.
I moved the filtering condition to the on clause. This keeps the left join intact.

Check if two products belong to same invoice

I have a table of Invoices, InvocicesLines and Products, and I'd like to select all the invoices that have two different products.
How can I do this?
Example:
Invoices table:
InvoiceNo | CustomerId | ...
=============+===============+========+
1 | 1 |
2 | 2 |
3 | 5 |
4 | 7 |
InvoicesLines table:
InvoiceNo (FK) | Id (PK) | ProductId |
===============+===============+============+
1 | 1 | 3 |
2 | 2 | 1 |
2 | 3 | 2 |
4 | 4 | 5 |
I need the invoices which have product 1 and 2:
InvoiceNo |
============+
2 |
You can join twice your invoices with your lines, and return the ones having different products. You must group the result.
select Invoices.IdInvoice
from Inovices
inner join InvoicesLines as First on First.IdInvoice = Invoices.IdInvoice
inner join InvoicesLines as Second on Second.IdInvoice = Invoice.IdInvoice
where First.IdProduct <> Second.IdProduct
group by Invoices.IdInvoice
This is going to return all the invoices with at least two different products.
But if you want to return the invoices with two different products, and only two different products, you can ensure it with a having clausule.
select Invoices.IdInvoice
from Inovices
inner join InvoicesLines as First on First.IdInvoice = Invoices.IdInvoice
inner join InvoicesLines as Second on Second.IdInvoice = Invoice.IdInvoice
where First.IdProduct <> Second.IdProduct
group by Invoices.IdInvoice
having count(Invoices.IdInvoice) = 2
Group by Invoice number and use having clause to return only those invoices which has distinct count of products equal to 2 (or greater of 1, if you want "at least two products"):
select i.InvoiceNumber
from Inoices i
inner join InvoiceLines il on il.InvoiceId = i.InoiceId
group by i.InvoiceNumber
having count(distinct il.ProductId) = 2

SQL Server - OR clause in join confusion

I have the table structure like below
Package
PACK_ID | DESCR | BRAND_ID
1 | Shoes | 20
2 | Cloths| NULL
ITEMS
ITEM_ID | PACK_ID | BRAND_ID
100 | 1 | 10
101 | 1 | NULL
102 | 1 | 10
BRANDS
NAME | BRAND_ID
A | 10
B | 20
I want to write a query to list how many items are there in a package grouped by same brand. If the brand is not defined in the item it should get it from package.
Note: Brand_id in both package and items are nullable
My query is this
SELECT count (*) as count,p.descr as descr,b.name FROM [items] item
inner join [package] p on item.pack_id= p.pack_id
inner join [brands] b on b.brand_id = item.brand_id or b.brand_id = p.brand_id
where p.pack_id = 1
group by b.name,p.descr
and my result is
COUNT | descr | NAME
2 | Shoes | a
3 | Shoes | B
whereas i expect the result to be something like this
COUNT | descr | NAME
2 | Shoes | a
1 | Shoes | B
could you please suggest what is wrong with my code? Thanks in advance.
Try using ISNULL on your join condition:
SELECT count (*) as count,p.pack_id as pack_id,b.name FROM [items] item
inner join [package] p on item.pack_id= p.pack_id
inner join [brands] b on b.brand_id = ISNULL(item.brand_id, p.brand_id)
where p.pack_id = 1
group by b.name,p.pack_id
Your OR was causing it to join to multiple rows, this should use the item by default and then fall back to the package.
I would tend to approach this by getting the brand for both the item and the package. Then decide which one to use in the select:
SELECT count(*) as count, p.descr as descr, coalesce(bi.name, bp.name) as name
FROM [items] item inner join
[package] p
on item.pack_id= p.pack_id left join
[brands] bi
on bi.brand_id = item.brand_id left join
brands bp
on b.brand_id = p.brand_id
where p.pack_id = 1
group by coalesce(bi.name, bp.name), p.descr;
One key advantage to this approach is performance. Databases tend to do a poor job when joins are on expression or or conditions.