I want to get the Catalog of a selected supplier along with the last purchase price which is the column Costs in the CommandDetails table as shown bellow :
Product (idproduct, productName)
Command (idCommand, CommandCode, CommandDate, idSupplier)
CommandDetails(idCommandDetails, idProduct, Qty, idCommand, Costs)
Supplier (idSupplier, SupplierName, SupplierAddress)
SupplierCatalog (idSupplier, idProduct)
I tried the row_number() Over (Partition by ...) and other methods but i'm missing something, my brain said enough.
Desired Result :
--------------------------------------------------
| SupplierName| ProductName | CommandDate | Costs|
--------------------------------------------------
| SUP1 | P1 | 01/01/2018 | 3,06 |
| SUP1 | P6 | 01/01/2018 | 1,65 |
| SUP1 | P8 | 03/01/2018 | 5,20 |
| SUP1 | P9 | 05/01/2018 | 8,00 |
| SUP1 | P10 | 01/01/2018 | NULL |
--------------------------------------------------
Null in Costs for P10 when the product has never been ordered.
My last attempt :
SELECT
*
FROM
(SELECT
Sct.idsupplier,
SCt.idProduct,
SCD.PurchasePriceCmd Costs,
SCD.Qty,
P.ProCode,
P.ProName,
Row_number() OVER(PARTITION BY Sct.idProduct order by P.ProCode) rn
FROM SupplierCatalog SCt
LEFT JOIN CommandDetails SCD
ON SCD.idProduct = SCat.idProduct
LEFT JOIN Command a
ON a.idCommand = SCD.idCommand
LEFT OUTER JOIN StoreCommand b
ON a.idCommand = b.idCommand
INNER JOIN Product P
ON P.idProduct = SCt.idProduct) t
where Sct.idSupplier = 4 and rn = 1
You could also try this:
SELECT
s.supplierName,
p.productName,
latestCommandDetail.CommandDate,
latestCommandDetail.Costs
FROM Supplier s
INNER JOIN SupplierCatalog sc ON sc.idSupplier = s.idSupplier
INNER JOIN Product p ON p.idProduct = sc.idProduct
OUTER APPLY
(
SELECT TOP 1
c.CommandDate,
cd.Costs
FROM Command c
INNER JOIN CommandDetails cd ON cd.idCommand = c.idCommand
WHERE c.idSupplier = s.idSupplier AND cd.idProduct = p.idProduct
ORDER BY c.CommandDate DESC
) latestCommandDetail
WHERE s.idSupplier = 4
ORDER BY
s.supplierName,
p.productName
I don't have SQL Server going on this machine at the moment so you may need to tweak the syntax. The trick is just doing a link to a subquery that returns the top row sorted by the date descending.
I'm assuming the idSupplier for "SUP1" is 4 as per you example code above.
OUTER APPLY (the other optional join) will return nulls if no record is returned from the sub query. If you are only interested in products with prices use CROSS APPLY instead.
Also note that this query does not determine what to do if:
- there are two Commands for the same last date from the same supplier for the same product but with different Costs
- the same product is sold twice under the same Command but at different Costs
In both cases this can probably be handled by extending the sort order of the sub-query or by grouping/aggregating the sub-query.
Something like this ought to work:
;with cte as (
select a.idCommand, b.idProduct, row_number() over (partition by b.idProduct
order by a.CommandDate desc) as rn, a.CommandDate, b.Costs
from Command a
inner join CommandDetails b on a.idCommand = b.idCommand
)
select
c.SupplierName, e.productName, cte.CommandDate, cte.Costs
from Supplier c
left join SupplierCatalog d on c.idSupplier = d.idSupplier
left join Product e on d.idProduct = e.idproduct
left join cte on e.idProduct = cte.idproduct and cte.rn = 1
where c.idSupplier = #SupplierNumber;
You can probably replace the common table expression at the top with a subquery, or take some fields out of the CTE and join them in later.
Related
Is it possible to emulate the following MySQL query:
SELECT * FROM `tbl` ORDER BY `date` DESC LIMIT X, 10
(X is a parameter)
in MS Access?
While the Access/JET TOP keyword does not directly provide an OFFSET capability, we can use a clever combination of TOP, a subquery, and a "derived table" to obtain the same result.
Here is an example for getting the 10 rows starting from offset 20 in a Person table in ORDER BY Name and Id...
SELECT Person.*
FROM Person
WHERE Person.Id In
(
SELECT TOP 10 A.Id
FROM [
SELECT TOP 30 Person.Name, Person.Id
FROM Person
ORDER BY Person.Name, Person.Id
]. AS A
ORDER BY A.Name DESC, A.Id DESC
)
ORDER BY Person.Name, Person.Id;
Essentially, we query the top 30, reverse the order, query the top 10, and then select the rows from the table that match, sorting in forward order again. This should be fairly efficient, assuming the Id is the PRIMARY KEY, and there is an index on Name. It might be that a specific covering index on Name, Id (rather than one on just Name) would be needed for best performance, but I think that indexes implicitly cover the PRIMARY KEY.
Another way - Let say you want from 1000 to 1999 records in a table called table1 (of course if you have that many records) you can do something like this.
MSSQL
SELECT *
FROM table1 LIMIT 1000, 1999;
MS Access
SELECT TOP 1000 *
FROM table1
Where ID NOT IN (SELECT TOP 999 table1.ID FROM table1);
To break this down
SELECT TOP NumA *
FROM table1
Where ID NOT IN (SELECT TOP NumB table1.ID FROM table1);
UpperLimit = 1999
LowerLimit = 1000
NumA = UpperLimit - LowerLimit + 1
ex. 1000 = 1999 - 1000 + 1
NumB = LowerLimit -1
ex. 999 = 1000 - 1
A better query would be:
SELECT Users.*
FROM Users
WHERE Users.id In
(
SELECT TOP X A.id
FROM [
SELECT TOP Y Users.*
FROM Users
ORDER BY Users.reg_date DESC
]. AS A
ORDER BY A.reg_date ASC
)
ORDER BY Users.reg_date DESC
Where
if((totalrows - offset) < limit) then
X = (totalrows - offset)
else
X = limit
And:
Y = limit + offset
For example, if total_rows = 12, and we set the limit to 10 (show 10 users per page), and the offset is calculated as p * limit - (limit) where p is the number of the current page, hence in the first page (p = 1) we will get: X = 12 and Y = 10, on the second X = 2 and Y = 20. The list of users is ordered by registration date (descending).
Simple and fastest solution.
myTable {ID*, Field2, Filed3...}
Assume your SortOrder contain primary KEY only
SELECT TOP PageItemsCount tb01.*
FROM myTable AS tb01
LEFT JOIN (
SELECT TOP OffsetValue ID FROM myTable ORDER BY ID ASC
) AS tb02
ON tb01.ID = tb02.ID
WHERE ISNULL(tb02.ID)
ORDER BY tb01.ID ASC
SortOrder based on other fields with duplicated values, in this case you must include your primary key in SortOrder as last one.
For exemple, myTable
+-------+--------+--------+
| ID | Field2 | Filed3 |
+-------+--------+--------+
| 1 | a1 | b |
| 2 | a | b2 |
| 3 | a1 | b2 |
| 4 | a1 | b |
+-------+--------+--------+
SELECT TOP 2 * From myTable ORDER BY FIELD2;
+-------+--------+--------+
| ID | Field2 | Filed3 |
+-------+--------+--------+
| 2 | a | b2 |
| 4 | a1 | b |
| 3 | a1 | b2 |
| 1 | a1 | b |
+-------+--------+--------+
SELECT TOP 2 * From myTable ORDER BY FIELD2, FIELD3;
+-------+--------+--------+
| ID | Field2 | Filed3 |
+-------+--------+--------+
| 2 | a | b2 |
| 4 | a1 | b |
| 1 | a1 | b |
+-------+--------+--------+
But if we add ID to sort order [AS LAST IN FIELDS LIST]
SELECT TOP 2 * From myTable ORDER BY FIELD2, ID;
+-------+--------+--------+
| ID | Field2 | Filed3 |
+-------+--------+--------+
| 2 | a | b2 |
| 1 | a1 | b |
+-------+--------+--------+
Final request
SELECT TOP PageItemsCount tb01.*
FROM myTable AS tb01
LEFT JOIN (
SELECT TOP OffsetValue ID FROM myTable ORDER BY Field2 ASC, ID
) AS tb02
ON tb01.ID = tb02.ID
WHERE ISNULL(tb02.ID)
ORDER BY tb01.Field2 ASC, tb01.ID
You can definitely get the the equivalent of "Limit" using the top keyword. See:
Access Database LIMIT keyword
No, JET SQL does not have a direct equivalent. As a workaround, you could add a WHERE clause that selects an ordered/id column between two values.
If possible, you can also use pass-through queries to an existing MySQL/other database.
While TOP in MS-Access can limit records returned, it does not take two parameters as with the MySQL LIMIT keyword (See this question).
I have two table as defined below,
Table: Customer:
ID | Customer_Name |Sex (bit)
--------------------------
1 | John | men
2 | Mack |women
3 | Andy |women
Table: Log:
ID | Customer_ID| Date
--------------------------
1 | 1 | 2020-06-03
2 | 3 |2020-06-03
I want to write a query to select each name with Sex condition and count of all Log placed by each customer using JOIN. the result will be,
1 | 1
2 | 0
3 | 0
My query:
(SELECT Customer.ID, COUNT(Log.Customer_ID) as number
from
Customer
Left JOIN
Log
on
Customer.Sex=0 and
Customer.ID=Log.Customer_ID
and
Log.Date>='2020-06-13'
group by Customer.ID)
But its returning incorrect results. Please advise
Use conditional aggregation
SELECT Customer.ID, COUNT(case Customer.Sex when 0 then Log.Customer_ID end) as number
from
Customer
Left JOIN
Log
on
Customer.ID=Log.Customer_ID
and
Log.Date>='2020-06-13'
group by Customer.ID
If you need only men/women move the predicate to WHERE
select Customer.ID, COUNT(Log.Customer_ID) as number
from Customer
left join Log
on Customer.ID=Log.Customer_ID
and Log.Date>='2020-06-13'
where Customer.Sex = 0
group by Customer.ID
Can you show the results this query is returning?
I think left join isn't a good approach, I would try with inner join instead if you want to filter in on clause (or just use where).
(SELECT Customer.ID, COUNT(Log.Customer_ID) as number
from Customer
JOIN Log on Customer.Sex=0 and Customer.ID=Log.Customer_ID and Log.Date>='2020-06-13'
group by Customer.ID)
I have this scheme
Document
DocumentId
Position
PositionId
DocumentId
Coordonate
CoordonateId
PositionId
Km
Road
RoadId
CoordonateId
Name
I need to order the documents by Road.Name and then by lowest Coordonate.Km. I tried to order them in SQL and EF but with no luck so now I use this code that is very slow:
foreach (var document in documents)
foreach (var position in documentPositions)
{
if (!position.Coordonates.Any())
continue;
var minKm =
position.Coordonates.OrderBy(a => a.Km).FirstOrDefault().Km.Value;
dictionary.Add(document, minKm);
break;
}
var sorted= dictionary.OrderBy(a => a.Key.RoadName).ThenBy(a => a.Value);
document.RoadName is a property that concatenates all the Road.Names that may be in the document.
LE:
I made a sqlfiddle at http://sqlfiddle.com/#!18/90b00/2/0 Hope that helps
You can write a subquery by Coordonates table, using rank function with windows function to get the samllest km, then do join
select Documents.DocumentId,
Documents.Name,
Roads.Name,
MIN(km) km
from Documents
INNER JOIN Positions ON Documents.DocumentId=Positions.DocumentId
INNER JOIN (
select
PositionId,
CoordonateId,
km,
rank() over(partition by PositionId order by Km) rn
from Coordonates
) Coordonates ON Positions.PositionId=Coordonates.PositionId and rn =1
INNER JOIN Roads ON Coordonates.CoordonateId=Roads.CoordonateId
group by Documents.DocumentId,
Documents.Name,
Roads.Name
Order By Roads.Name,km
[Results]:
| DocumentId | Name | Name | km |
|------------|------|------|----|
| 2 | Doc2 | A1 | 10 |
| 1 | Doc1 | A2 | 10 |
| 3 | Doc3 | A2 | 15 |
sqlfiddle
You can find min value without using OrderBy and then FirstOrDefault Instead use Min which should be a little bit faster.
var minKm = position.Coordinates.Min(coord => coord.Km.Value);
Update: maybe your code is slow because position.Coordinates is not loaded from the database and your code does extra database roundtrip for every document which is why it is slow. Please ensure all data is already loaded and the foreach loop is actually causing that slowness. If coordinates data aren't included you can add Include(...) statement into your EF query.
I have three tables in sql server-
Photos table:
----------------------
| PhotoId | PhotoName|
----------------------
| | |
----------------------
Tags table:
----------------------
| TagId | TagName|
----------------------
| | |
----------------------
Junction table:
----------------------
| PhotoId | TagId |
----------------------
| | |
----------------------
A photo can have multiple tags, and a tag can belong to multiple photos.
Now, for example Eagle has tags Bird, Animal, Wild. I would like to search with tags Domestic, Bird, Animal. I'm using sql statement where TagName='Domestic' OR TagName='Bird' OR TagName='Animal'. The problem is, it produces query result where Eagle comes two times.
But I would like to have Eagle only once. Any idea?
SELECT p.*
FROM photos p
WHERE EXISTS ( SELECT 'a'
FROM junction j
JOIN tags t ON t.TagId = j.TagId
WHERE p.PhotoId = j.PhotoId
AND t.TagName IN ('Domestic', 'Bird', 'Animal')
)
select distinct p.*
from photos p
inner join Junction j on j.PhotoId = p.PhotoId
inner join tags t on j.TagId = t.TagId
where t.TagName in ('Domestic', 'Bird','Animal')
The quickest solution i can think of, you just only need to specify the name of the photo then do a distinct, then each Name of the animal will appear once based on the tag.
SELECT DISTINCT PhotoName
FROM Tags T
JOIN Junction J ON J.TagId = T.TagId
JOIN Photos P ON P.PhotoId = J.PhotoId
WHERE TagName=('Domestic' OR TagName='Bird') OR TagName='Animal'
with DISTINCT each photo should appear only once in result set
select * from Photos
where PhotoId in
( select DISTINCT PhotoId
from Junction
where TagId in
(select TagId from Tags where TagName='Domestic' OR TagName='Bird' OR TagName='Animal')
)
I have a table with "customer_id, date, installment_no, amount" columns. I want to get the information of last installment of each customer_id till today. here installment_no is int type and when a new installment is deposited, the installment_no is increased by 1 in new entry. My table look like:
CS1001 | 12-06-2013 | 1 | 2500
CS1002 | 19-06-2013 | 1 | 1600
CS1001 | 14-07-2013 | 2 | 2500
I want to get a sqlcommand statement for do so.
Group all records by customer_id, then order all customer's records by installment_no, and select only record with max installment_no from each group:
from c in customers
group c by c.customer_id into g
select g.OrderByDescending(x => x.installment_no).First()
Same with pure SQL if you don't use Linq
SELECT c.* FROM Customers c
INNER JOIN (
SELECT customer_id, MAX(installment_no) max_installment
FROM Customers
GROUP BY customer_id
) cmax
ON c.customer_id = cmax.customer_id
AND c.installment_no = cmax.max_installment
var result = list.GroupBy(x=>x.customer_id)
.Select(g=>g.OrderByDescending(y=>y.installment_no).First())
.ToList();