I am trying to produce an output where only the person with Surname = "Berry" lessons are shown, with Date in Ascending order.
SELECT c.booking_number AS "Reference",
c.ldate AS "Lesson Date",
s.first_name AS "Instructor First Name",
s.surname AS "Instructor Last Name"
FROM am_lessons c
join am_staff s
ON ( c.staff_id = c.staff_id )
join am_bookings m
ON ( c.booking_number = m.booking_number )
join am_customers f
ON ( f.customer_id = c.customer_id )
WHERE f.surname = 'Berry'
ORDER BY c.ldate ASC
The Statement runs but produces repetitions of "Booking Numbers" and "First Name" and "Second Name".
Firstly, you have some extra/incorrect joins:
SELECT c.booking_number AS "Reference",
c.ldate AS "Lesson Date",
s.first_name AS "Instructor First Name",
s.surname AS "Instructor Last Name"
FROM am_lessons c
join am_staff s
ON ( c.staff_id = s.staff_id ) -- should be s.Staff_Id
-- JOIN AM_Bookings m -- this join is not needed
-- ON (c.Booking_Number = m.Booking_Number)
join am_customers f
ON ( f.customer_id = c.customer_id )
WHERE f.surname = 'Berry'
ORDER BY c.ldate ASC
Secondly, you may have duplicate rows in your tables. If this is the case, either fix the data, or add a DISTINCT modifier.
Note: It is possible that the join on AM_Bookings was being used to filter out records that have no associated Booking_Number. If this is the case, you can simply filter on c.Booking_Number IS NOT NULL.
Related
I am trying to filter cg_group names (please check the query) and group (using: GROUP BY) the results according to last updated opportunity (using: ORDER BY opportunities.date_modified DESC).
When I used query without use group by it returns the following results:
SELECT cg_groups.name
FROM cg_groups
JOIN cg_groups_cstm ON cg_groups_cstm.id_c = cg_groups.id
JOIN accounts_cstm ON cg_groups.name = accounts_cstm.client_group_c
JOIN accounts ON accounts.id = accounts_cstm.id_c
JOIN accounts_opportunities ON accounts.id = accounts_opportunities.account_id
JOIN opportunities ON accounts_opportunities.opportunity_id = opportunities.id
WHERE cg_groups.deleted='0' AND cg_groups_cstm.status_c='1' AND opportunities.deleted='0'
ORDER BY opportunities.date_modified DESC
Results:
ABC Group
ABC Group
CBC Group
ABC Group
XYZ Group
But I want to group this to following order:
ABC Group
CBC Group
XYZ Group
To do that I added GROUP BY cg_groups.name
SELECT cg_groups.name
FROM cg_groups
JOIN cg_groups_cstm ON cg_groups_cstm.id_c = cg_groups.id
JOIN accounts_cstm ON cg_groups.name = accounts_cstm.client_group_c
JOIN accounts ON accounts.id = accounts_cstm.id_c
JOIN accounts_opportunities ON accounts.id = accounts_opportunities.account_id
JOIN opportunities ON accounts_opportunities.opportunity_id = opportunities.id
WHERE cg_groups.deleted='0' AND cg_groups_cstm.status_c='1' AND opportunities.deleted='0'
GROUP BY cg_groups.name
ORDER BY opportunities.date_modified DESC
But now I get this error:
Msg 8127, Level 16, State 1, Line 10
Column "opportunities.date_modified" is invalid in the ORDER BY clause because it is not contained in either an aggregate function or the GROUP BY clause.
Someone please help me to solve this issue, thank you.
Use ROW_NUMBER to find the most recently updated record for each group:
WITH cte AS (
SELECT cg_groups.name, o.date_modified,
ROW_NUMBER() OVER (PARTITION BY o.date_modified DESC) rn
FROM cg_groups cg
INNER JOIN cg_groups_cstm cgc
ON cgc.id_c = cg.id
INNER JOIN accounts_cstm ac
ON cg.name = ac.client_group_c
INNER JOIN accounts a
ON a.id = ac.id_c
INNER JOIN accounts_opportunities ao
ON a.id = ao.account_id
INNER JOIN opportunities o
ON ao.opportunity_id = o.id
WHERE cg.deleted = '0' AND cgc.status_c = '1' AND o.deleted = '0'
)
SELECT name
FROM cte
WHERE rn = 1
ORDER BY date_modified DESC;
Note that this may not be exactly what you want. This answer returns a single record per name group which is the most recently updated for that group. It then orders all results descending, but maybe you want ascending.
put opportunities.date_modified in selection and group by then you can use that in order by
SELECT opportunities.date_modified,cg_groups.name
FROM cg_groups
JOIN cg_groups_cstm ON cg_groups_cstm.id_c = cg_groups.id
JOIN accounts_cstm ON cg_groups.name = accounts_cstm.client_group_c
JOIN accounts ON accounts.id = accounts_cstm.id_c
JOIN accounts_opportunities ON accounts.id = accounts_opportunities.account_id
JOIN opportunities ON accounts_opportunities.opportunity_id = opportunities.id
WHERE cg_groups.deleted='0' AND cg_groups_cstm.status_c='1' AND opportunities.deleted='0'
GROUP BY cg_groups.name,opportunities.date_modified
ORDER BY opportunities.date_modified DESC
but for your result you can try like below just use distinct
SELECT distinct cg_groups.name
FROM cg_groups
JOIN cg_groups_cstm ON cg_groups_cstm.id_c = cg_groups.id
JOIN accounts_cstm ON cg_groups.name = accounts_cstm.client_group_c
JOIN accounts ON accounts.id = accounts_cstm.id_c
JOIN accounts_opportunities ON accounts.id = accounts_opportunities.account_id
JOIN opportunities ON accounts_opportunities.opportunity_id = opportunities.id
WHERE cg_groups.deleted='0' AND cg_groups_cstm.status_c='1' AND opportunities.deleted='0'
order by cg_groups.name
no group by need as you have not used any aggregate function
how about just adding distinct right after your SELECT statement .
Select distinct ... from ...
I am trying not to show duplicate values in the ORDER column from the results list; however, I am showing duplicate "ORDER" values. How can I fix this?
select i.contractnumber as "CONTRACT NUMBER", t.ordernumber as
"ORDER", t.title as "ORDER TITLE", c.companyname as "COMPANY",
LEFT(cs.firstname, 1) +'. '+ cs.lastname as "TECHINICAN",
REPLACE(cs.CONTACT,'703-735-', 'x') as "CONTACT (703) 735....)"
from Company c inner join CONTRACTS i
on i.Company_ID = c.Company_ID
inner join Orders t
on t.ordernumberid = i.ordernumberid
inner join hub rut
on rut.titleOrder_ID = t.titleOrder_ID
inner join hub2 r
on r.Role_ID = rut.Role_ID
inner join Customer u
on u.Customer_ID = rut.Customer_ID
inner join CORD cs
on cs.CORD_ID = u.CORD_ID
inner join Method cv
on cv.Method_ID = i.Method_ID
where cs.Contact LIKE '%703-735-%'
and cv.Method_ID = 1;
The simple way to fix this is with a group by -- eg GROUP BY Order however if you have more than one value for the other columns you will have an issue -- you can pick a value -- for example max. That would like like this:
select
max(i.contractnumber) as "CONTRACT NUMBER",
t.ordernumber as "ORDER",
max(mat.title) as "ORDER TITLE",
max(c.companyname) as "COMPANY",
max(LEFT(cs.firstname, 1) +'. '+ cs.lastname) as "TECHINICAN",
max(REPLACE(cs.CONTACT,'703-735-', 'x')) as "CONTACT (703) 735....)"
-- etc as you have
-- ...
-- end with
GROUP BY t.ordernumber
I have the following requisites for a query:
Needs to ordered on a inner joined table (see from_products_products below),
Allow duplicates names on from_products_products
It cannot return duplicates records on the origin table (distinct on products.id).
The following query will eliminate the duplicate names, which is not desired, as I had to put a distinct on from_products_products.name because of the use in order by:
SELECT DISTINCT ON (from_products_products.name, products.id) "products".* FROM "products"
INNER JOIN "suppliers_plugin_source_products" ON "suppliers_plugin_source_products"."to_product_id" = "products"."id"
INNER JOIN "products" "from_products_products" ON "from_products_products"."id" = "suppliers_plugin_source_products"."from_product_id"
INNER JOIN "suppliers_plugin_source_products" "sources_from_products_products_join" ON "sources_from_products_products_join"."to_product_id" = "products"."id"
INNER JOIN "suppliers_plugin_suppliers" ON "suppliers_plugin_suppliers"."id" = "sources_from_products_products_join"."supplier_id"
WHERE "products"."profile_id" = 45781 AND (("products"."type" IN ('SuppliersPlugin::DistributedProduct') OR "products"."type" IS NULL)) AND (products.archived <> true)
ORDER BY from_products_products.name ASC, products.id
Using GROUP BY has the same effect and also don't remove duplicates;
The original query that gives duplicate products when the INNER JOIN doesn't match any product:
SELECT "products".* FROM "products"
INNER JOIN "suppliers_plugin_source_products" ON "suppliers_plugin_source_products"."to_product_id" = "products"."id"
INNER JOIN "products" "from_products_products" ON "from_products_products"."id" = "suppliers_plugin_source_products"."from_product_id"
INNER JOIN "suppliers_plugin_source_products" "sources_from_products_products_join" ON "sources_from_products_products_join"."to_product_id" = "products"."id"
INNER JOIN "suppliers_plugin_suppliers" ON "suppliers_plugin_suppliers"."id" = "sources_from_products_products_join"."supplier_id"
WHERE "products"."profile_id" = 45781 AND (("products"."type" IN ('SuppliersPlugin::DistributedProduct') OR "products"."type" IS NULL)) AND (products.archived <> true)
ORDER BY from_products_products.name ASC
So, how to overcome this on PostgreSQL?
PS: This is part of open-source software Noosfero-ecosol
Does this do what you want?
with t as (
SELECT DISTINCT ON (products.id) "products".*,
from_products_products.name as from_products_name
FROM "products"
INNER JOIN "suppliers_plugin_source_products" ON "suppliers_plugin_source_products"."to_product_id" = "products"."id"
INNER JOIN "products" "from_products_products" ON "from_products_products"."id" = "suppliers_plugin_source_products"."from_product_id"
INNER JOIN "suppliers_plugin_source_products" "sources_from_products_products_join" ON "sources_from_products_products_join"."to_product_id" = "products"."id"
INNER JOIN "suppliers_plugin_suppliers" ON "suppliers_plugin_suppliers"."id" = "sources_from_products_products_join"."supplier_id"
WHERE "products"."profile_id" = 45781 AND (("products"."type" IN ('SuppliersPlugin::DistributedProduct') OR "products"."type" IS NULL)) AND (products.archived <> true)
ORDER BY products.id
)
select t.*
from t
order by from_products_name
It seems to meet your requirements.
EDIT:
If the above does what you want, I can think of five options:
The above using a CTE.
Basically the same logic, using a subquery.
Using window functions, which is structurally very similar.
Using group by.
Using a where clause for the filtering logic.
Here is the group by method:
SELECT "products".*,
MIN(from_products_products.name) as from_products_name
FROM "products"
INNER JOIN "suppliers_plugin_source_products" ON "suppliers_plugin_source_products"."to_product_id" = "products"."id"
INNER JOIN "products" "from_products_products" ON "from_products_products"."id" = "suppliers_plugin_source_products"."from_product_id"
INNER JOIN "suppliers_plugin_source_products" "sources_from_products_products_join" ON "sources_from_products_products_join"."to_product_id" = "products"."id"
INNER JOIN "suppliers_plugin_suppliers" ON "suppliers_plugin_suppliers"."id" = "sources_from_products_products_join"."supplier_id"
WHERE "products"."profile_id" = 45781 AND (("products"."type" IN ('SuppliersPlugin::DistributedProduct') OR "products"."type" IS NULL)) AND (products.archived <> true)
GROUP BY products.id
ORDER BY from_products_name;
This form depends on products.id being declared as a primary key. Alternatively, you can put all the columns from that table in the group by.
Rewriting (simplifying the aliases) yields:
SELECT p1.*
FROM products p1
INNER JOIN suppliers_plugin_source_products spsp
ON spsp.to_product_id = p1.id
INNER JOIN products p2
ON p2.id = spsp.from_product_id
INNER JOIN suppliers_plugin_source_products spsp2
ON spsp2.to_product_id = p1.id -- <<-- Huh?
INNER JOIN suppliers_plugin_suppliers sps
ON sps.id = spsp2.supplier_id
WHERE p1.profile_id = 45781
AND (p1."type" IN ('SuppliersPlugin::DistributedProduct') OR p1."type" IS NULL)
AND p1.archived <> true
ORDER BY p2.name ASC -- <<-- Huh?
;
The outer query only refers to the product tables p1 and p2.
Assuming that JOINing the "suppliers_plugin_source_products" table twice was unintentional, this can be reduced to:
SELECT p1.*
FROM products p1
JOIN products p2
ON EXISTS (
SELECT * FROM suppliers_plugin_source_products spsp
-- the next line might not be necessary ...
INNER JOIN suppliers_plugin_suppliers sps ON sps.id = spsp.supplier_id
WHERE spsp.to_product_id = p1.id
AND spsp.from_product_id = p2.id
)
WHERE p1.profile_id = 45781
AND (p1."type" IN ('SuppliersPlugin::DistributedProduct') OR p1."type" IS NULL)
AND p1.archived <> true
ORDER BY p2.name ASC
;
I have a working SELECT that returns info on each company (i.e. subscriber). Certain descriptive data is returned from inner joins on "code" tables. My problem is how to add a new column to the result which is a count of the number of users belonging to each subscriber. SubscriberID in the user table is a foreign key:
ALTER TABLE dbo.UserInfo WITH CHECK ADD CONSTRAINT FK_UserInfo_SubscriberID
FOREIGN KEY(SubscriberID) REFERENCES dbo.Subscriber (SubscriberID)
ON UPDATE CASCADE
ON DELETE CASCADE
I added the LEFT OUTER JOIN below and the GROUP BY but I cannot figure out why the parser complains that U.SubscriberID is an invalid column name).
SELECT SubscriberID
,SubscriberName
,DaysUntilExpired
,SubscriptionStatus SubscriptionStatusCode
, C.Description SubscriptionStatus
,U.NumberUsers
FROM dbo.Subscriber S
JOIN dbo.CodeValue C ON S.SubscriptionStatus = C.Value
JOIN dbo.CodeNamespace N ON N.ID = C.CodeNamespaceID AND N.Name = 'SubscriptionStatus'
JOIN dbo.CodeValue V ON S.NotificationStatus = V.Value
JOIN dbo.CodeNamespace X ON X.ID = V.CodeNamespaceID AND X.Name = 'NotificationStatus'
LEFT OUTER JOIN (SELECT Count(*) AS NumberUsers FROM dbo.UserInfo) AS U
ON S.SubscriberID = U.SubscriberID
GROUP BY
S.SubscriberID
,SubscriberName
,DaysUntilExpired
,S.SubscriptionStatus
,SubscriptionStatus
You are probably looking for the following. You need to inline the subquery
SELECT SubscriberID
,SubscriberName
,DaysUntilExpired
,SubscriptionStatus SubscriptionStatusCode
, C.Description SubscriptionStatus
,(SELECT Count(*) AS NumberUsers FROM dbo.UserInfo where SubscriberID = S.SubscriberID) AS NumberUsers
FROM dbo.Subscriber S
JOIN dbo.CodeValue C ON S.SubscriptionStatus = C.Value
JOIN dbo.CodeNamespace N ON N.ID = C.CodeNamespaceID AND N.Name = 'SubscriptionStatus'
JOIN dbo.CodeValue V ON S.NotificationStatus = V.Value
JOIN dbo.CodeNamespace X ON X.ID = V.CodeNamespaceID AND X.Name = 'NotificationStatus'
GROUP BY
S.SubscriberID
,SubscriberName
,DaysUntilExpired
,S.SubscriptionStatus
,SubscriptionStatus
Sql complains because your LEFT OUTER JOIN subquery does not include SubscriberId in column list (and in a group by clause since you have a COUNT aggregate), so it can't make the join on this column.
You should do:
LEFT OUTER JOIN (SELECT SubscriberID , Count(*) AS NumberUsers FROM dbo.UserInfo GROUP BY SubscriberID ) AS U
ON S.SubscriberID = U.SubscriberID
The query below provides me with all the information I need. However, I am getting multiple results due to revision levels. The first and only revision for most of the resulst returns a blank. If a quote has been revised, it's assigned revision A. If it's revised again, it gets a B. I'm getting results for blank, A, B, etc. I would like to limit the query to only see the greatest value only if it's not blank.
SELECT
RTRIM(CUST_REF_NUMBER) AS "PICKUP #"
,RTRIM(SN.NUMBER) AS "NUMBER"
,DATE(PICKUP_TSTAMP) AS "PICKUP DATE"
,B.DUE_DATE
,WEIGHT
,TOTAL_PIECES AS "PIECES"
,V.QUOTE_ID AS "QUOTE"
,EQUIP AS "ACTUAL QUOTE PRICE"
,TOTAL_CHG AS "CHARGES"
,B.CUST AS "ACCOUNT NUMBER"
,C.ACCT_SPELLING AS "ACCOUNT NAME"
,H.REVISION
FROM AF.NOTIF SN
INNER JOIN AF.BILL B
ON SN.NUMBER = B.NUMBER
AND B.ARCHIVE_KEY = ''
AND B.CORRECTED = ''
AND B.TSTAMP >= SN.TSTAMP - 10 DAYS
INNER JOIN AF.PROS V
ON SN.NUMBER = V.NUMBER
INNER JOIN AF.CHARGES H
ON V.QUOTE_ID = H.QUOTE_ID
AND H.STATUS = 'X'
LEFT OUTER JOIN AF.SPELLING C
ON B.CUST = C.ACCT_NUM
AND C.TYPE = 'M'
WHERE WEB_ID IN ('XXXXXXX','XXXXXXX','XXXXXXX')
AND SN.TSTAMP > TIMESTAMP(CHAR(CURRENT DATE - 7 DAYS)||'-00.00.00.000000')
ORDER BY PICKUP_TSTAMP
Something like this, may be:
SELECT * FROM (
SELECT
RTRIM(CUST_REF_NUMBER) AS "PICKUP #"
,RTRIM(SN.NUMBER) AS "NUMBER"
,DATE(PICKUP_TSTAMP) AS "PICKUP DATE"
,B.DUE_DATE
,WEIGHT
,TOTAL_PIECES AS "PIECES"
,V.QUOTE_ID AS "QUOTE"
,EQUIP AS "ACTUAL QUOTE PRICE"
,TOTAL_CHG AS "CHARGES"
,B.CUST AS "ACCOUNT NUMBER"
,C.ACCT_SPELLING AS "ACCOUNT NAME"
,H.REVISION
,ROW_NUMBER() OVER (PARTITION BY V.QUOTE_ID ORDER BY H.REVISION DESC) RN
FROM AF.NOTIF SN
INNER JOIN AF.BILL B
ON SN.NUMBER = B.NUMBER
AND B.ARCHIVE_KEY = ''
AND B.CORRECTED = ''
AND B.TSTAMP >= SN.TSTAMP - 10 DAYS
INNER JOIN AF.PROS V
ON SN.NUMBER = V.NUMBER
INNER JOIN AF.CHARGES H
ON V.QUOTE_ID = H.QUOTE_ID
AND H.STATUS = 'X'
LEFT OUTER JOIN AF.SPELLING C
ON B.CUST = C.ACCT_NUM
AND C.TYPE = 'M'
WHERE WEB_ID IN ('XXXXXXX','XXXXXXX','XXXXXXX')
AND SN.TSTAMP > TIMESTAMP(CHAR(CURRENT DATE - 7 DAYS)||'-00.00.00.000000')
) T
WHERE T.RN = 1
ORDER BY T.PICKUP_TSTAMP
Try approaching this in two conceptual query steps, by using a common table expression [CTE]. First get your query results as you have been, but rank the rows in order of the most recent (highest) revision. Then SELECT only rows from that CTE that are the most recent.
Once this is done, you might move some of the joins down to the second section. I have attempted to demonstrate moving one for you, but you may be able move more.
WITH q as
(SELECT RTRIM(CUST_REF_NUMBER) AS "PICKUP #"
,RTRIM(SN.NUMBER) AS "NUMBER"
,DATE(PICKUP_TSTAMP) AS "PICKUP DATE"
,B.DUE_DATE
,WEIGHT
,TOTAL_PIECES AS "PIECES"
,H.QUOTE_ID AS "QUOTE"
,EQUIP AS "ACTUAL QUOTE PRICE"
,TOTAL_CHG AS "CHARGES"
,B.CUST AS "ACCOUNT NUMBER"
,H.REVISION
,ROW_NUMBER() OVER (PARTITION BY H.QUOTE_ID
ORDER BY H.REVISION DESC) AS AGEING
FROM AF.NOTIF SN
JOIN AF.BILL B
ON SN.NUMBER = B.NUMBER
AND B.ARCHIVE_KEY = ''
AND B.CORRECTED = ''
AND B.TSTAMP >= SN.TSTAMP - 10 DAYS
JOIN AF.PROS V
ON SN.NUMBER = V.NUMBER
JOIN AF.CHARGES H
ON H.QUOTE_ID = V.QUOTE_ID
AND H.STATUS = 'X'
WHERE WEB_ID IN ('XXXXXXX','XXXXXXX','XXXXXXX')
AND SN.TSTAMP > TIMESTAMP(CHAR(CURRENT DATE - 7 DAYS)||'-00.00.00.000000')
)
SELECT
"PICKUP #"
,"NUMBER"
,"PICKUP DATE"
, DUE_DATE
, WEIGHT
,"PIECES"
,"QUOTE"
,"ACTUAL QUOTE PRICE"
,"CHARGES"
,"ACCOUNT NUMBER"
,C.ACCT_SPELLING AS "ACCOUNT NAME"
, REVISION
FROM q
LEFT OUTER
JOIN AF.SPELLING C
ON B.CUST = C.ACCT_NUM
AND C.TYPE = 'M'
WHERE AGEING = 1
ORDER BY PICKUP_DATE