Is there a way to distinct multiple columns in sql? - sql

Is there a way to distinct multiple columns? When I tried to do it with p.name it says that there is an error that occurred.
SELECT DISTINCT( V.NAME ),
POH.status,
poh.shipdate,
pod.orderqty,
POD.receivedqty,
POD.rejectedqty,
p.NAME
FROM purchasing.vendor v
INNER JOIN purchasing.productvendor pv
ON v.businessentityid = pv.businessentityid
INNER JOIN production.product p
ON pv.productid = P.productid
INNER JOIN purchasing.purchaseorderdetail POD
ON P.productid = POD.productid
INNER JOIN purchasing.purchaseorderheader POH
ON POD.purchaseorderid = POH.purchaseorderid
ORDER BY v.NAME,
p.NAME;

If you want one row per NAME, then you can use ROW_NUMBER():
with q as (
<your query here with columns renamed so there are no duplicates>
)
select q.*
from (select q.*,
row_number() over (partition by v_name order by v_name) as seqnum
from q
) q
where seqnum = 1;

DISTINCT is not a function, it is an operator and its scope is the entire SELECT clause
(The query formatting is just for emphasizing the point)
SELECT DISTINCT
V.NAME,
POH.status,
poh.shipdate,
pod.orderqty,
POD.receivedqty,
POD.rejectedqty,
p.NAME
FROM purchasing.vendor v
...
That answers the error you get, however, I doubt if this will give you the results you are looking for

Related

Get max/min value of the column independet from where clause

I am having the following query and running it on postgress
select
p.id as p_id,
p.name as p_name,
p.tags,
p.creator,
p.value
p.creation_date,
cp.id as c_part_id,
fr.distance
count(*) OVER() AS total_item
from t_p p
left join t_c_part cp on p.id = cp.p_id
left join t_fl fr on p.id = fr.p_id
where p.name = 'test'
ORDER BY p.id ASC, p.name ASC
OFFSET 0 FETCH NEXT 25 ROWS only
What is missing here is that I also need to get max(p.value) and min(p.value) not affected by the "where" clause - so calculated from total (all) values.
I am dreaming that I can do it within one query and reduce the number of transactions.
Honestly not sure if it is possible!
What I tried is something like this ->
SELECT
(SELECT COUNT(*) from t_p) as count,
(SELECT json_agg(t.*) FROM (
SELECT * FROM t_p
where ***
) AS t) AS rows
But this one did not look really nice as it require additional JSON manipulation at the backend.
I discovered that I might try to use the "with" statement to create a temporary view so the where condition is only evaluated once, but did not succeed to make it works...
You can add the extra columns as scalar subqueries in the form (select min(value) from t_p). Their values are not related to the main query so they should be totally independent.
Your original query has some minor syntax issues (missing commas). I fixed those and the result is:
select
p.id as p_id,
p.name as p_name,
p.tags,
p.creator,
p.value,
p.creation_date,
cp.p_id as c_part_id,
fr.distance,
count(*) OVER() AS total_item,
(select min(value) from t_p) as min_value,
(select max(value) from t_p) as max_value
from t_p p
left join t_c_part cp on p.id = cp.p_id
left join t_fl fr on p.id = fr.p_id
where p.name = 'test'
ORDER BY p.id ASC, p.name ASC
OFFSET 0 FETCH NEXT 25 ROWS only
See running query (without any data) at DB Fiddle.
You can join to a sub-query that calculates both MIN & MAX.
...
from t_p p
left join t_c_part cp on p.id = cp.p_id
left join t_fl fr on p.id = fr.p_id
cross join (
select
min(value) as min_value
, max(value) as max_value
, avg(value) as avg_value
from t_p
) as v
...
Then use v.min_value and v.max_value in the select.
Doesn't even have to be a LATERAL.
You could get the minimum and maximum "on the side" like this:
select
p.id as p_id,
p.name as p_name,
p.tags,
p.creator,
p.value
p.creation_date,
cp.id as c_part_id,
fr.distance,
count(*) OVER() AS total_item,
p.min_value,
p.max_value
from (SELECT id,
name,
tags,
creator,
value,
creation_date,
min(value) OVER () AS min_value,
max(value) OVER () AS max_value,
FROM t_p) AS p
left join t_c_part cp on p.id = cp.p_id
left join t_fl fr on p.id = fr.p_id
where p.name = 'test'
ORDER BY p.id ASC, p.name ASC
OFFSET 0 FETCH NEXT 25 ROWS only;

How can I get sum of Items from one table to another in Sql

How can I get sum of items from one table to another in sql
use join and subquery
select p.*,stock from theProduct p
join ( select prodcode,sum(Qty) as stock from thePurchaseDetail group by prodcode) t
on p.prodcode=t.prodcode
You can use APPLY :
select p.*, pp.stock
from theProduct p cross apply
(select sum(pp.qty) as stock
from thePurchaseDetail pp
where pp.prodcode = p.prodcode
) pp;
You can also use window function with JOIN :
select p.*, sum(pp.qty) over (partition by p.prodcode) as stock
from theProduct p inner join
thePurchaseDetail pp
on pp.prodcode = p.prodcode;
You would left join in case there are missing prodcodes in tblPurchaseDetail table
select p.*
,t.stock
from tblProduct p
left join ( select prodcode
,sum(Qty) as stock
from tblPurchaseDetail
group by prodcode
) t
on p.prodcode=t.prodcode

Selecting the Id of the item with a MAX value when doing a left join

Each product in the database can have many revisions. Some products might not have any revisions at all.
ProductRevision table has the following fields: Id, Version, SubmitDate
I am trying to figure out how I can select a field called LatestRevisionId based on the MAX SubmitDate and if not revision then the field will be null
SELECT p.Id, p.Name, p.Price
FROM Product p
LEFT OUTER JOIN ProductRevision pr ON p.Id = pr.ProductId
Do I have to do a sub select in my select? I really want to try and use HAVING but can't figure out how to do it with a left join.
I was trying to do the following as a sub select:
(SELECT Id
FROM ProductRevision
WHERE ProductId=p.Id
HAVING SubmitDate=MAX(SubmitDate)
) AS LatestVersionId
Please note that I am using SQL SERVER 2008
Here's one option using row_number:
SELECT *
FROM (
SELECT p.Id, p.Name, p.Price, pr.id as LatestRevisionId,
row_number() over (partition by p.Id order by pr.SubmitDate desc) rn
FROM Product p
LEFT OUTER JOIN ProductRevision pr PN p.Id = pr.ProductId
) t
WHERE rn = 1
This will select a single Product with the latest matching row from the ProductRevision table.
If you just prefer to use max, then you need to join the table back to itself again:
SELECT p.Id, p.Name, p.Price, pr.id as LatestRevisionId
FROM Product p
LEFT OUTER JOIN ProductRevision pr PN p.Id = pr.ProductId
LEFT OUTER JOIN (SELECT ProductId, MAX(SubmitDate) MaxSubmitDate
FROM ProductRevision
GROUP BY ProductId) mpr ON pr.ProductId = mpr.ProductId AND
pr.SubmitDate = mpr.MaxSubmitDate
This could perhaps return duplicates though if multiple revisions share the same date.
if you are using SQL Server 2012 and aboe, below code will give you desired result.
SELECT DISTINCT p.Id, p.Name, p.Price, FIRST_VALUE(pr.ID) OVER (PARTITION BY p.Id ORDER BY pr.SubmitDate DESC) AS LatestVersionId
FROM Product p
LEFT OUTER JOIN ProductRevision pr ON p.Id = pr.ProductId
You can use a LEFT JOIN like this:
SELECT p.Id, p.Name, p.Price, pr.RevisionId as LatestRevisionId
FROM Product p LEFT OUTER JOIN
(SELECT pr.*,
ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY SubmitDate DESC) as seqnum
FROM ProductRevision pr
)
ON p.Id = pr.ProductId AND seqnum = 1;
If you want to aggregation other values, then just do:
SELECT p.Id, p.Name, p.Price,
MAX(CASE WHEN seqnum = 1 THEN pr.RevisionId END) as LatestRevisionId
FROM Product p LEFT OUTER JOIN
(SELECT pr.*,
ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY SubmitDate DESC) as seqnum
FROM ProductRevision pr
)
ON p.Id = pr.ProductId
GROUP BY p.Id, p.Name, p.Price;
This should be the simplest method to accomplish what you want.

Inner joining to a select statement where the inner select statement's where clause references the outer select?

This is a slimmed down query of my greater problem, but the gist is that I'm trying to inner join to a select where the select is limited by the outer select. Is that possible? I'm getting an error about multipart identifier S.Item and S.SerialNum on the inner select.
The gist is this, we have to group by the item/serial, and the query is big enough, we don't want to go back and group everything in the entire query for this minor join.
SELECT S.Item, S.SerialNum, S.ReceiveDate
FROM SALES S
INNER JOIN (SELECT W.Item, W.SerialNum, MIN(W.SalesDate)
FROM WARRANTY W
WHERE W.Item = S.Item AND
W.SerialNum = S.SerialNum
GROUP BY Item, SerialNum, SalesDate) WW
ON S.Item = WW.Item AND WW.SerialNum
Looks like you have your JOIN reference in the wrong place.
SELECT S.Item, S.SerialNum, S.ReceiveDate
FROM SALES S
INNER JOIN
(
SELECT W.Item, W.SerialNum, MIN(W.SalesDate) MinSalesDate
FROM WARRANTY W
GROUP BY Item, SerialNum
) WW
ON S.Item = WW.Item
AND S.SerialNum = WW.SerialNum
Edit, based on your comment about filtering, you can place a WHERE clause on your inner SELECT:
SELECT S.Item, S.SerialNum, S.ReceiveDate, WW.MinSalesDate
FROM SALES S
INNER JOIN
(
SELECT W.Item, W.SerialNum, MIN(W.SalesDate) MinSalesDate
FROM WARRANTY W
WHERE yourFilter here
GROUP BY Item, SerialNum
) WW
ON S.Item = WW.Item
AND S.SerialNum = WW.SerialNum
You can try a common_table_expression instead of a JOIN also. Check the WITH clause here.
It could be something like this:
WITH Warranty_CTE (Item, SerialNum, MinSalesDate)
AS
(
SELECT W.Item, W.SerialNum, MIN(W.SalesDate) MinSalesDate
FROM WARRANTY W
GROUP BY Item, SerialNum
)
SELECT S.Item, S.SerialNum, S.ReceiveDate
FROM SALES S
INNER JOIN Warranty_CTE WC
ON S.Item = WC.Item
AND S.SerialNum = WC.SerialNum

Compare subselect value with value in master select

In MS Access, I have a query where I want to use a column in the outer query as a condition in the inner query:
SELECT P.FirstName, P.LastName, Count(A.attendance_date) AS CountOfattendance_date,
First(A.attendance_date) AS FirstOfattendance_date,
(SELECT COUNT (*)
FROM(SELECT DISTINCT attendance_date
FROM tblEventAttendance AS B
WHERE B.event_id=8
AND B.attendance_date >= FirstOfattendance_date)
) AS total
FROM tblPeople AS P INNER JOIN tblEventAttendance AS A ON P.ID = A.people_id
WHERE A.event_id=8
GROUP BY P.FirstName, P.LastName
;
The key point is FirstOfattendance_date - I want the comparison deep in the subselect to use the value in each iteration of the master select. Obviously this doesn't work, it asks me for the value of FirstOfattendance_date when I try to run it.
I'd like to do this without resorting to VB code... any ideas?
How about:
SELECT
p.FirstName,
p.LastName,
Count(a.attendance_date) AS CountOfattendance_date,
First(a.attendance_date) AS FirstOfattendance_date,
c.total
FROM (
tblPeople AS p
INNER JOIN tblEventAttendance AS a ON
a.people_id = p.ID)
INNER JOIN (SELECT people_id, Count (attendance_date) As total
FROM (
SELECT DISTINCT people_id,attendance_date
FROM tblEventAttendance)
Group By people_id) AS c ON
p.ID = c.people_id
GROUP BY
p.ID, c.total;
Can you change
B.attendance_date >= FirstOfattendance_date
to
B.attendance_date >= First(A.attendance_date)