SELECT first row in recursive part of table expression - sql

I tried to get the first row for a WHERE statement in a recursive table expression. Sadly i'm getting this error:
The TOP or OFFSET operator is not allowed in the recursive part of a recursive common table expression 'cteTree'.
Here's my SQL Query:
WITH cteTree AS(
SELECT
cct.CategoryID AS bla,
ct.CategoryID,ct.ParentCategoryID,
ct.Name,
ct.Published,
CASE
WHEN EXISTS(SELECT ProductID FROM Product WHERE ProductID = (SELECT TOP(1) ProductID FROM ProductCategory WHERE ProductCategory.CategoryID = ct.CategoryID) AND Product.Published = 1) THEN 1
ELSE 0
END AS ProductExists,
1 As Cycle
FROM Category AS ct
LEFT JOIN Category AS cct ON cct.ParentCategoryID = ct.CategoryID
WHERE cct.CategoryID IS NULL
UNION ALL
SELECT
cct.CategoryID AS bla,
ct.CategoryID,
ct.ParentCategoryID,
ct.Name,
ct.Published,
CASE
WHEN cct.ProductExists = 1 THEN 1
WHEN EXISTS(SELECT ProductID FROM Product WHERE ProductID = (SELECT TOP(1) ProductID FROM ProductCategory WHERE ProductCategory.CategoryID = ct.CategoryID) AND Product.Published = 1) THEN 1
ELSE 0
END AS ProductExists,
Cycle + 1
FROM Category AS ct
JOIN cteTree AS cct ON ct.CategoryID = cct.ParentCategoryID
)
SELECT * FROM cteTree
The problem is in the second Case statement under UNION ALL.
SELECT ProductID FROM Product WHERE ProductID = (SELECT TOP(1) ProductID FROM ProductCategory WHERE ProductCategory.CategoryID = ct.CategoryID) AND Product.Published = 1
Does someone know if there's another expression for selecting the first row in a recursive Table expression that works?

There are some tricks to use TOP (1) in a CTE, such as using ROW_NUMBER instead, or putting it into a TVF. But in your case you can just use a normal join:
You should also use NOT EXISTS instead of the LEFT JOIN IS NULL construct.
WITH cteTree AS(
SELECT
cct.CategoryID AS bla,
ct.CategoryID,ct.ParentCategoryID,
ct.Name,
ct.Published,
CASE
WHEN EXISTS (SELECT 1
FROM Product p
JOIN ProductCategory pc ON pc.CategoryID = ct.CategoryID
WHERE p.Published = 1
) THEN 1
ELSE 0
END AS ProductExists,
1 As Cycle
FROM Category AS ct
WHERE NOT EXISTS (SELECT 1
FROM Category AS cct
WHERE cct.ParentCategoryID = ct.CategoryID
)
UNION ALL
SELECT
cct.CategoryID AS bla,
ct.CategoryID,
ct.ParentCategoryID,
ct.Name,
ct.Published,
CASE
WHEN cct.ProductExists = 1 THEN 1
WHEN EXISTS (SELECT 1
FROM Product p
JOIN ProductCategory pc ON pc.CategoryID = ct.CategoryID
WHERE p.Published = 1
) THEN 1
ELSE 0
END AS ProductExists,
Cycle + 1
FROM Category AS ct
JOIN cteTree AS cct ON ct.CategoryID = cct.ParentCategoryID
)
SELECT *
FROM cteTree

Related

Combine 3 UNIONed queries into one

I have the following which I would like to do without UNIONs so that the string split is only happening once.
I would also like the results to be in one line per MemberId showing all 3 counts rather than 3 rows.
SELECT MemberKey, 'login' as countType, count(MemberKey) as total FROM [dbo].[CA_MembersAudit]
WHERE isSuccess = 1 and MemberKey IN (SELECT value FROM STRING_SPLIT( #userList, ','))
Group By MemberKey
UNION
SELECT MemberId as MemberKey, 'articles' as countType, count(MemberId) as total FROM [dbo].[CA_Activities]
WHERE StateId = 'Opened' and MemberId IN (SELECT value FROM STRING_SPLIT( #userList, ','))
Group By MemberId
UNION
SELECT MemberId as MemberKey,'assessments' as countType, count(MemberId) as total FROM [dbo].[CA_Activities]
WHERE PercentageComplete is not null AND MemberId IN (SELECT value FROM STRING_SPLIT( #userList, ','))
Group By MemberId
UNION
Can anyone suggest how I should amend the queries into one to be able to do this?
You could use a subquery for each total:
select m.MemberKey,
(select count(*) from CA_MembersAudit ma where m.MemberKey = ma.MemberKey and ma.isSuccess = 1) as 'login_total',
(select count(*) from CA_Activities a where m.MemberKey = a.MemberId and a.stateId = 'Opened') as 'articles_total',
(select count(*) from CA_Activities a where m.MemberKey = a.MemberId and a.PercentageComplete is not null) as 'assessments_total'
from (select value as MemberKey from STRING_SPLIT('1,2,3,4', ',')) m
If your tables have a primary key, you could also do something like this:
select m.MemberKey,
count(distinct ma.Id) 'login_total',
count(distinct a1.Id) 'articles_total',
count(distinct a2.Id) 'assessments_total'
from (select value as MemberKey from STRING_SPLIT('1,2,3,4', ',')) m
left outer join CA_MembersAudit ma on m.MemberKey = ma.MemberKey and ma.isSuccess = 1
left outer join CA_Activities a1 on m.MemberKey = a1.MemberId and a1.stateId = 'Opened'
left outer join CA_Activities a2 on m.MemberKey = a2.MemberId and a2.PercentageComplete is not null
group by m.MemberKey
I believe you can use a CTE and then JOIN to each of the UNION participants.
WITH MemberList AS (
SELECT value AS Member
FROM STRING_SPLIT(#userList, ',')
)
SELECT
MemberKey
,'login' AS countType
,count(MemberKey) AS total
FROM [dbo].[CA_MembersAudit]
JOIN MemberList
ON MemberList.Member = CA_MembersAudit.MemberKey
WHERE isSuccess = 1
GROUP BY MemberKey
UNION
SELECT
MemberId AS MemberKey
,'articles' AS countType
,count(MemberId) AS total
FROM [dbo].[CA_Activities]
JOIN MemberList
ON MemberList.Member = CA_Activities.MemberId
WHERE StateId = 'Opened'
GROUP BY MemberId
UNION
SELECT
MemberId AS MemberKey
,'assessments' AS countType
,count(MemberId) AS total
FROM [dbo].[CA_Activities]
JOIN MemberList
ON MemberList.Member = CA_Activities.MemberId
WHERE PercentageComplete IS NOT NULL
GROUP BY MemberId;
try this :
With MemberList as (
SELECT value as ID FROM STRING_SPLIT( #userList, ',')
),
Activities as (
select f1.MemberId, sum(case when f1.StateId = 'Opened' then 1 else 0 end) as TotalOpened,
sum(case when f1.PercentageComplete is not null then 1 else 0 end) as TotalPercentageComplete
FROM [dbo].[CA_Activities] f1 inner join MemberList f2 on f1.MemberId=f2.ID
where f1.StateId = 'Opened' or f1.PercentageComplete is not null
group by f1.MemberId
),
MemberAudit as (
SELECT f1.MemberKey, count(*) as TotalSuccess
FROM [dbo].[CA_MembersAudit] f1 inner join MemberList f2 on f1.MemberKey=f2.ID
WHERE f1.isSuccess = 1
Group By f1.MemberKey
)
select f1.*, isnull(f2.TotalOpened, 0) as TotalOpened, isnull(f2.TotalPercentageComplete, 0) as TotalPercentageComplete, isnull(f3.TotalSuccess, 0) as TotalSuccess
from MemberList f1
left outer join Activities f2 on f1.ID=f2.MemberId
left outer join MemberAudit f3 on f1.ID=f3.MemberKey
other solution :
SELECT f1.value as ID, isnull(f2.TotalOpened, 0) as TotalOpened, isnull(f2.TotalPercentageComplete, 0) as TotalPercentageComplete, isnull(f3.TotalSuccess, 0) as TotalSuccess
FROM STRING_SPLIT( #userList, ',') f1
outer apply
(
select sum(case when f1.StateId = 'Opened' then 1 else 0 end) as TotalOpened,
sum(case when f1.PercentageComplete is not null then 1 else 0 end) as TotalPercentageComplete
FROM [dbo].[CA_Activities] f1
where (f1.StateId = 'Opened' or f1.PercentageComplete is not null) and f1.MemberId=f1.value
) f2
outer apply
(
SELECT count(*) as TotalSuccess FROM [dbo].[CA_MembersAudit] f1 WHERE f1.isSuccess = 1 and f1.MemberKey=f1.value
) f3

How to get only one value when you are getting multiple values against key in SQL

I have a SQL query. Below is the query
select
ID,
replace(replace(replace(replace([Name],',',''),'"',''),':',''),'?','') [Name] ,
replace(replace([Description],',',''),'"','') [Description],
[GUID],
Bussinesskey
from course
where COURSESTATUS = 'Published' and RETIREDTF = 0
and
bussinesskey in
(
...
)
and id in (
select c.id from course c
inner join COURSE_CUSTOMERENTITLEMENT cce on cce.COURSE_ID = c.ID
inner join CUSTOMERENTITLEMENT ce on ce.id = cce.CUSTOMERENTITLEMENT_ID
where
ce.ENROLLMENTTYPE = 'Course'
and ce.customer_id = 23753
and c.COURSESTATUS = 'Published' and c.RETIREDTF = 0
UNION
select c.id from course c
inner join COURSE_COURSEGROUP cg on cg.course_id = c.id
inner join COURSEGROUP_CUSTOMERENTITLEMENT cgce on cgce.COURSEGROUP_ID = cg.COURSEGROUP_ID
inner join CUSTOMERENTITLEMENT ce on ce.id = cgce.CUSTOMERENTITLEMENT_ID
where
ce.ENROLLMENTTYPE = 'CourseGroup'
and ce.customer_id = 23753
and c.COURSESTATUS = 'Published' and c.RETIREDTF = 0
)
order by name, id asc
When this query runs then I get the output like the following snapshot
You can see in the screen shot that I am getting 8 names of same type(Contracts). The last id of Contracts is 780697 which is the latest record that is added to database. Now i want that when my query runs then it gets only the latest record. Means instead of showing 8 name of Contarcts. Its only the show the latest one for each course name. Means for Contracts only record with ID 780697 is shown. If other courses has the same result then there latest Id record is shown only. How can I achieve this ?
Thanks
You can try following for achieving latest ID:-
select MAX(ID),
replace(replace(replace(replace([Name],',',''),'"',''),':',''),'?','') [Name] ,
replace(replace([Description],',',''),'"','') [Description],
[GUID],
Bussinesskey
from course
where COURSESTATUS = 'Published' and RETIREDTF = 0
and
bussinesskey in
(
...
)
and id in (
select c.id from course c
inner join COURSE_CUSTOMERENTITLEMENT cce on cce.COURSE_ID = c.ID
inner join CUSTOMERENTITLEMENT ce on ce.id = cce.CUSTOMERENTITLEMENT_ID
where
ce.ENROLLMENTTYPE = 'Course'
and ce.customer_id = 23753
and c.COURSESTATUS = 'Published' and c.RETIREDTF = 0
UNION
select c.id from course c
inner join COURSE_COURSEGROUP cg on cg.course_id = c.id
inner join COURSEGROUP_CUSTOMERENTITLEMENT cgce on cgce.COURSEGROUP_ID = cg.COURSEGROUP_ID
inner join CUSTOMERENTITLEMENT ce on ce.id = cgce.CUSTOMERENTITLEMENT_ID
where
ce.ENROLLMENTTYPE = 'CourseGroup'
and ce.customer_id = 23753
and c.COURSESTATUS = 'Published' and c.RETIREDTF = 0
)
group by [Name], [Description], [GUID], Bussinesskey
order by name, id asc
Here how I did this. I don't know how efficient this is but it's giving me what I am exactly wanting
select ID, [Name], [Description], [GUID], Bussinesskey from (
select row_number() over (partition by [Name] order by id desc) as row, t1.* from (
select
ID,
replace(replace(replace(replace([Name],',',''),'"',''),':',''),'?','') [Name] ,
replace(replace([Description],',',''),'"','') [Description],
[GUID],
Bussinesskey
from course
where COURSESTATUS = 'Published' and RETIREDTF = 0
and bussinesskey in (
'PSTOAS0314001',
...
'RECEAL0510019'
)
and id in (
select c.id from course c
inner join COURSE_CUSTOMERENTITLEMENT cce on cce.COURSE_ID = c.ID
inner join CUSTOMERENTITLEMENT ce on ce.id = cce.CUSTOMERENTITLEMENT_ID
where ce.ENROLLMENTTYPE = 'Course'
and ce.customer_id = 23753
and c.COURSESTATUS = 'Published' and c.RETIREDTF = 0
UNION
select c.id from course c
inner join COURSE_COURSEGROUP cg on cg.course_id = c.id
inner join COURSEGROUP_CUSTOMERENTITLEMENT cgce on cgce.COURSEGROUP_ID = cg.COURSEGROUP_ID
inner join CUSTOMERENTITLEMENT ce on ce.id = cgce.CUSTOMERENTITLEMENT_ID
where ce.ENROLLMENTTYPE = 'CourseGroup'
and ce.customer_id = 23753
and c.COURSESTATUS = 'Published' and c.RETIREDTF = 0
)
)t1
) t
where t.row=1
order by t.name, t.id asc
Thanks
;with Course as
(
Select 50000 ID, 'Contracts' Name, '<BLURB>' [Description], '<GUID>' GUID, 'ARB1' BusinessKey
UNION
Select 60000 ID, 'Contracts' Name, '<BLURB>' [Description], '<GUID>' GUID, 'ARB2' BusinessKey
UNION
Select 70000 ID, 'Contracts' Name, '<BLURB>' [Description], '<GUID>' GUID, 'ARB3' BusinessKey
UNION
Select 80000 ID, 'Contracts' Name, '<BLURB>' [Description], '<GUID>' GUID, 'ARB4' BusinessKey
UNION
Select 90000 ID, 'Contracts' Name, '<BLURB>' [Description], '<GUID>' GUID, 'ARB5' BusinessKey
UNION
Select 40000 ID, 'NOT Contracts' Name, '<BLURB>' [Description], '<GUID>' GUID, 'ARB1' BusinessKey
),
Course_Group AS
(
Select 50000 Course_ID, 1 COURSEGROUP_ID
UNION
Select 60000 Course_ID, 1 COURSEGROUP_ID
UNION
Select 70000 Course_ID, 1 COURSEGROUP_ID
UNION
Select 80000 Course_ID, 1 COURSEGROUP_ID
UNION
Select 90000 Course_ID, 2 COURSEGROUP_ID
UNION
Select 40000 Course_ID, 1 COURSEGROUP_ID
),
CourseGroup_CustomerEntitlement AS
(
Select 1 COURSEGROUP_ID, 1 CUSTOMERENTITLEMENT_ID
--UNION
--Select 2 COURSEGROUP_ID, 2 CUSTOMERENTITLEMENT_ID
--UNION
--Select 2 COURSEGROUP_ID, 4 CUSTOMERENTITLEMENT_ID
),
Course_CustomerEntitlement AS
(
Select 50000 Course_ID, 3 CUSTOMERENTITLEMENT_ID
UNION
Select 60000 Course_ID, 3 CUSTOMERENTITLEMENT_ID
UNION
Select 90000 Course_ID, 3 CUSTOMERENTITLEMENT_ID
UNION
Select 40000 Course_ID, 4 CUSTOMERENTITLEMENT_ID
),
CustomerEntitlement AS
(
Select 1 ID, 23753 Customer_ID, 'CourseGroup' ENROLLMENTTYPE
UNION
Select 2 ID, 7 Customer_ID, 'NOT COURSE GROUP' ENROLLMENTTYPE
UNION
Select 3 ID, 23753 Customer_ID, 'Course' ENROLLMENTTYPE
UNION
Select 4 ID, 7 Customer_ID, 'NOT COURSE' ENROLLMENTTYPE
),
CTE_base_data as (
SELECT
C.ID,
replace(replace(replace(replace(C.[Name],',',''),'"',''),':',''),'?','') [Name] ,
replace(replace(C.[Description],',',''),'"','') [Description],
C.[GUID],
C.Businesskey
FROM Course C
JOIN Course_Group CG ON CG.Course_ID = C.ID
JOIN CourseGroup_CustomerEntitlement CG_CE ON CG_CE.COURSEGROUP_ID = CG.COURSEGROUP_ID
JOIN CustomerEntitlement CE_GROUP ON CE_GROUP.ID = CG_CE.CUSTOMERENTITLEMENT_ID
AND CE_GROUP.ENROLLMENTTYPE = 'CourseGroup'
WHERE
CE_GROUP.Customer_ID = 23753
UNION
SELECT
C.ID,
replace(replace(replace(replace([Name],',',''),'"',''),':',''),'?','') [Name] ,
replace(replace([Description],',',''),'"','') [Description],
[GUID],
Businesskey
FROM Course c
JOIN Course_CustomerEntitlement CCE ON CCE.Course_ID = C.ID
JOIN CustomerEntitlement CE_COURSE ON CE_COURSE.ID = CCE.CUSTOMERENTITLEMENT_ID
AND CE_COURSE.ENROLLMENTTYPE = 'COURSE'
WHERE
CE_COURSE.Customer_ID = 23753
--and COURSESTATUS = 'Published'
--and RETIREDTF = 0
--and businesskey in
-- (
-- ...
-- )
),
CTE_max_data as (
Select name, max(ID) ID
from CTE_base_data
group by name
)
Select Data.*
from CTE_base_data Data
JOIN CTE_max_data Filter
ON Data.ID = Filter.ID
AND Data.Name = Filter.Name
order by name, id asc
This yields:
ID Name Description GUID Businesskey
90000 Contracts ARB5
40000 NOT Contracts ARB1
Please advise where my data assumptions fall flat.

Error using CTE in SQLITE3

I use these codes to get level(depth) of my parent/child categories table. and the isLeaf attribute:
with cteCat as (
select
id, parent,
[cteLevel] = 1
from Categories
where 1=1 and parent=0
union all
select
c.id, c.parent,
[cteLevel] = cc.cteLevel+1
from Categories c
join cteCat cc
on c.parent = cc.id
where 1=1
and c.parent <> 0
)
select
*
, CASE WHEN EXISTS (SELECT * FROM Categories c2 WHERE c2.parent = c1.id) THEN 0 ELSE 1 END AS IsLeaf
, (SELECT COUNT(*) FROM Categories c2 WHERE c2.parent = c1.id) AS Leafs
from cteCat c1
order by c1.id
result is:
id parent cteLevel IsLeaf Leafs
1 0 1 0 2
....
it's OK in SQLSERVER. but when i execute at sqlite, I get Error:
Error while executing SQL query on database 'ado':
no such column: cc.cteLevel
Any help? thanks.
SQLite does not support variable assignments like this.
However, you should be able to do the same with standard SQL:
WITH cteCat AS (
SELECT id,
parent,
1 AS cteLevel
FROM ...
WHERE ...
UNION ALL
SELECT c.id,
c.parent,
cc.cteLevel + 1
FROM ...
WHERE ...
)
SELECT ...

How to properly join TOP 1 fields to SQL query

How can I properly join the fields I have in the comment block to the SQL query? I have a billing phone number on the order header, but on the order lines, there is a shipping phone number for each line. The billing and shipping can be different.
On every order line of one order, it's 99% the same shipping number, but I want to do Top 1 and not group by just incase some data gets messed up.
I think UNION might get me what I want, but it seems like there is a better way to get everything in one query and not copying & pasting the same "where" clauses.
SELECT a.Order_no
,a.Customer_no
,a.BILL_LAST_NAME
,a.BILL_FIRST_NAME
,b.email
,a.BILL_ADDRESS1
,a.BILL_ADDRESS2
,a.BILL_CITY
,a.BILL_STATE
,a.BILL_POSTAL_CODE
,a.BILL_COUNTRY
,b.Address_Type
,a.BILL_PHONE
,a.BILL_PHONE_EXT
,a.Order_Date
,a.billing_status
,a.PO_Number
,a.Customer_comments
,a.ShipMethodShipperDesc
,a.ShipRate
,a.CouponDiscountCode
,a.CouponDiscount
,a.CustomerDiscount
,a.CustomerDiscountPercent
,a.SalesTaxTotal
,a.Payment_Method
,a.Credit_Card_Type
,a.Credit_Card_Number
,a.Order_Date
,a.BILL_TYPE
,a.Order_Net
/* I added these lines but would like them joined properly */
/*-------->*/
, (select top 1 SHIP_ADDRESS1 from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_ADDRESS2 from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_CITY from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_STATE from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_POSTAL_CODE from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_COUNTRY from LineItems C where c.ORDER_NO = a.ORDER_NO)
/*<-----------*/
FROM Orders AS a
,Customers AS b
WHERE a.customer_no = b.customer_no
AND a.AccountName = 'mywebaccount'
AND a.billing_status <> 'Canceled'
AND a.transferred = 0
AND a.order_status <> 'Canceled'
AND EXISTS (
SELECT *
FROM LineItems c
WHERE c.order_no = a.order_no
)
ORDER BY a.order_date
,a.order_no
Firstly, don't do FROM sometable1 AS t1, sometable2 AS t2. Always join explicitly. Secondly, from the way your case looks, some variant of APPLY would suit nicely.
Here's my version:
SELECT a.Order_no
,a.Customer_no
,a.BILL_LAST_NAME
,a.BILL_FIRST_NAME
,b.email
,a.BILL_ADDRESS1
,a.BILL_ADDRESS2
,a.BILL_CITY
,a.BILL_STATE
,a.BILL_POSTAL_CODE
,a.BILL_COUNTRY
,b.Address_Type
,a.BILL_PHONE
,a.BILL_PHONE_EXT
,a.Order_Date
,a.billing_status
,a.PO_Number
,a.Customer_comments
,a.ShipMethodShipperDesc
,a.ShipRate
,a.CouponDiscountCode
,a.CouponDiscount
,a.CustomerDiscount
,a.CustomerDiscountPercent
,a.SalesTaxTotal
,a.Payment_Method
,a.Credit_Card_Type
,a.Credit_Card_Number
,a.Order_Date
,a.BILL_TYPE
,a.Order_Net
,li.SHIP_ADDRESS1
,li.SHIP_ADDRESS2
,li.SHIP_CITY
,li.SHIP_STATE
,li.SHIP_POSTAL_CODE
,li.SHIP_COUNTRY
FROM Orders AS a
INNER JOIN Customers AS b
ON a.customer_no = b.customer_no
CROSS APPLY
(
SELECT TOP 1 c.SHIP_ADDRESS1
,c.SHIP_ADDRESS2
,c.SHIP_CITY
,c.SHIP_STATE
,c.SHIP_POSTAL_CODE
,c.SHIP_COUNTRY
FROM LineItems c
WHERE c.ORDER_NO = a.ORDER_NO
ORDER BY c.Id -- or whatever
) AS li
WHERE a.AccountName = 'mywebaccount'
AND a.billing_status <> 'Canceled'
AND a.transferred = 0
AND a.order_status <> 'Canceled'
-- no need for that exists since CROSS APPLY works like INNER JOIN
ORDER BY a.order_date,a.order_no
I suggest something like:
SELECT Order_no
,Customer_no
,BILL_LAST_NAME
,BILL_FIRST_NAME
...
,SHIP_ADDRESS1
,SHIP_ADDRESS2
,SHIP_CITY
,SHIP_STATE
,SHIP_POSTAL_CODE
,SHIP_COUNTRY
FROM (
SELECT a.Order_no
,a.Customer_no
,a.BILL_LAST_NAME
,a.BILL_FIRST_NAME
,b.email
,a.BILL_ADDRESS1
,a.BILL_ADDRESS2
,a.BILL_CITY
,a.BILL_STATE
,a.BILL_POSTAL_CODE
,a.BILL_COUNTRY
,b.Address_Type
,a.BILL_PHONE
,a.BILL_PHONE_EXT
,a.Order_Date
,a.billing_status
,a.PO_Number
,a.Customer_comments
,a.ShipMethodShipperDesc
,a.ShipRate
,a.CouponDiscountCode
,a.CouponDiscount
,a.CustomerDiscount
,a.CustomerDiscountPercent
,a.SalesTaxTotal
,a.Payment_Method
,a.Credit_Card_Type
,a.Credit_Card_Number
,a.Order_Date
,a.BILL_TYPE
,a.Order_Net
,c.SHIP_ADDRESS1
,c.SHIP_ADDRESS2
,c.SHIP_CITY
,c.SHIP_STATE
,c.SHIP_POSTAL_CODE
,c.SHIP_COUNTRY
, row_number() over (partition by a.order_no
order by SHIP_ADDRESS1, SHIP_ADDRESS2, SHIP_CITY, SHIP_STATE
, SHIP_POSTAL_CODE, SHIP_COUNTRY) as rn
FROM Orders AS a
JOIN Customers AS b
ON a.customer_no = b.customer_no
JOIN numberLineItems c
ON c.order_no = a.order_no
WHERE a.AccountName = 'mywebaccount'
AND a.billing_status <> 'Canceled'
AND a.transferred = 0
AND a.order_status <> 'Canceled'
) as x
WHERE rn = 1
ORDER BY order_date
, order_no;
I created a simple example and posted
HERE on SQL Fiddle
The simplest way is just to join with the top 1 from a subquery
select cus.*, ord.*, sub.*
from Customer cus
join [Order] ord on ord.CustomerId = cus.Id
join (
select it.OrderId, it.ItemName, it.ItemAddress
from Items it
where it.Id in (select MAX(id) from Items group by OrderId)
) as sub on sub.OrderId = ord.Id
If you want to see the first item instead the last you can replace MAX(id) with MIN(id)
Note the only "problem" is if there are no items for the order it ill not show.
But if you got orders without a item and still want to show it you can use a left join
Se the fiddle for a running example.
I hope you don't mind I used explicit joins =)

Selecting Parent Rows With Multiple Conditions

I had most of this query down until a new condition arose and it has confounded me. Given the following simplified table schema:
Parent Table:
ID
FName
LName
Child Table:
[Index]
ParentID
Active_Flag
ExpirationDate
What I want to do is get Parent rows for which:
There are no children.
There are children whose Active_Flag is 1 but whose expiration dates are blank or NULL.
There are indeed children but none have the Active_Flag set to 1.
The following query came up with my first two criteria:
SELECT p.ID, p.LNAME, p.FNAME,
CASE
WHEN COUNT(ct.indx) = 0 THEN 'None'
WHEN ct.ExpirationDate is NULL or ct.ExpirationDate = '' THEN 'No expiration date'
END AS Issue
FROM ParentTable AS p
LEFT JOIN ChildTable ct
ON p.ID = ct.ParentID
GROUP BY p.ID, p.LNAME, p.FNAME, ct.[INDEX], ct.ExpirationDate
HAVING (COUNT(ct.[INDEX]) = 0) OR (ct.ExpirationDate IS NULL OR ct.ExpirationDate = '')
ORDER BY p.LNAME
I don't know how to account for #3. Any help is appreciated. Thanks in advance.
You can also do this in the HAVING clause:
SELECT p.ID, p.LNAME, p.FNAME,
(CASE WHEN COUNT(ct.indx) = 0 THEN 'None'
WHEN ct.ExpirationDate is NULL or ct.ExpirationDate = '' THEN 'No expiration date'
WHEN sum(case when ActiveFlag = 1 then 1 else 0 end) = 0 then 'No active children'
END) AS Issue
FROM ParentTable p LEFT JOIN
ChildTable c
ON p.ID = ct.ParentID
GROUP BY p.ID, p.LNAME, p.FNAME
HAVING (COUNT(ct.[INDEX]) = 0) OR
(ct.ExpirationDate IS NULL OR ct.ExpirationDate = '') or
sum(case when ActiveFlag = 1 then 1 else 0 end) = 0
ORDER BY p.LNAME
The DISTINCT in the SELECT is redundant. You do not need it with an aggregation.
You can simplify the having to "sum(ActiveFlag)" if the activeFlag is indeed an integer. If not, then it should be "= '1'" rather than "= 1'.
you can use unions for this query.....
I am not sure about this query ....but it will help you in solving your prolem
select p.id,p.lname,p.fname from parents where (p.id not in (select pid from children))
union
select p.id,p.lname,p.fname from parents p,children c inner join on c.pid=p.id where c.active_flag=1 and c.expiration_date is null or c.expiration_date=''
union
select p.id,p.lname,p.fname from parents p inner join on c.pid=p.id where c.active_flag<>1;
SELECT *
FROM parenttable pt
-- condition 1: There should be no parents without children at all
WHERE NOT EXISTS (
SELECT * FROM childtable c1
WHERE c1.parent_id = pt.id
)
-- condition 2: There should be no children with a flag but without a date
OR EXISTS (
SELECT * FROM childtable c2
WHERE c2.parent_id = pt.id
AND c2.active_flag = 1 AND c2.expire_date IS NULL
)
-- condition 3: There should at least be a child with the active_flag
OR NOT EXISTS (
SELECT * FROM childtable c3
WHERE c3.parent_id = pt.id
AND c2.active_flag = 1
)
;
-- Active flags for children c1 and c2
-- c1 c2 (X= child doesn't exists)
-----+-----+---+
-- X X rule1+rule3 # no children at all
-- 0 X rule3 # only one child
-- 1 X # idem
-- 0 0 rule 3 # two children
-- 0 1
-- 1 0
-- 1 1
--
-- , which means that rule3 implies rule1, and rule1 is redundant
-------------------------
SELECT *
FROM parenttable pt
-- condition 1+3: There should at least be a child with the active_flag
WHERE NOT EXISTS (
SELECT * FROM childtable c1
WHERE c1.parent_id = pt.id
AND c1.active_flag = 1
)
-- condition 2: There should be no children with a flag but without a date
OR EXISTS (
SELECT * FROM childtable c2
WHERE c2.parent_id = pt.id
AND c2.active_flag = 1 AND c2.expire_date IS NULL
)
;