SQL WHERE Temporary Column - sql

I have this MSSQL Query which works
SELECT c.CategoryName + ' (' + cast(count(ic.CategoryId) as varchar(255)) + ')' AS
CategoryName, count(ic.CategoryId) AS NumPhotos,
c.Slug, c.ParentCategoryId, c.Id
FROM Categories
c LEFT JOIN
ItemCategories ic
on ic.CategoryId = c.Id
GROUP BY c.CategoryName, c.slug, c.ParentCategoryId, c.id
ORDER BY ParentCategoryId DESC
And I want to return only rows, WHERE NumPhotos>0 but temporary columns are not allowed on SQL WHERE clause

The having clause is the canonical solution to your problem. However, I suspect that an inner join would also be appropriate:
SELECT c.CategoryName + ' (' + cast(count(ic.CategoryId) as varchar(255)) + ')' AS CategoryName,
count(ic.CategoryId) AS NumPhotos,
c.Slug, c.ParentCategoryId, c.Id
FROM Categories c INNER JOIN
ItemCategories ic
on ic.CategoryId = c.Id
GROUP BY c.CategoryName, c.slug, c.ParentCategoryId, c.id
ORDER BY ParentCategoryId DESC;
Without sample data, it is hard to be sure, but I'm pretty sure this does the same thing.
The advantage of an inner join is that it might be more efficient, because less (maybe only slightly less) data would be processed and the optimizer has more opportunities to pick the best join algorithm.

Existing comments have given adequate answers, but here is another solution, using a "virtual table":
SELECT * FROM (
SELECT c.CategoryName + ' (' + cast(count(ic.CategoryId) as varchar(255)) + ')' AS
CategoryName, count(ic.CategoryId) AS NumPhotos,
c.Slug, c.ParentCategoryId, c.Id
FROM Categories
c LEFT JOIN
ItemCategories ic
on ic.CategoryId = c.Id
GROUP BY c.CategoryName, c.slug, c.ParentCategoryId, c.id
)
WHERE NumPhotos>0
ORDER BY ParentCategoryId DESC

Related

SQL IN "Too many values"

I have a problem I want to get all of the DISTINCT CLIENTIDs in my LCMINV table in which the CLIENTID is in LAAPPL's CLIENTID and LACOBORW's COBORWID and also the APPLICATION STATUS is either 'PEN' OR 'REC'.
Here is my table:
++++++++++++++++++++++++++++++++++++++++++++
+ LAAPPL + LACOBORW + LCMINV +
++++++++++++++++++++++++++++++++++++++++++++
+ CLIENTID + APPLNO + CLIENTID +
+ APPLNO + COBORWID + +
+ STATUS + + +
++++++++++++++++++++++++++++++++++++++++++++
Here is my SQL Query I've done so far:
SELECT DISTINCT(CLIENTID)
FROM LCMINV
WHERE CLIENTID
IN (SELECT CLIENTID, COBORWID
FROM LAAPPL
LEFT OUTER JOIN LACOBORW ON LACOBORW.APPLNO = LAAPPL.APPLNO
WHERE STATUS = 'PEN' OR STATUS = 'REC')
Can it be done in one query or do I need separate queries? I tried my query above an I am getting an error "Too many values"
I would probably use a CTE to break the query up into smaller parts, and then use INNER JOINs to combine the tables. Since You query LAAPPL, I put both columns used into a single subquery, and then join on each column in the next CTE.
It would look something like this:
WITH CTE_LAAPPL AS
(
SELECT
CLIENTID
,APPLNO
FROM LAAPPL
WHERE STATUS IN ('PEN','REC')
)
,CLIENTIDs AS
(
SELECT DISTINCT
l.COBORWID AS CLIENTID
FROM CTE_LAAPPL c
INNER JOIN LACOBORW l ON l.APPLNO = c.APPLNO
UNION
SELECT DISTINCT
l.CLIENTID
FROM CTE_LAAPPL c
INNER JOIN LCMINV l ON l.CLIENTID = c.CLIENTID
)
SELECT
c.CLIENTID
,c.NAME
FROM LCCLIENT c
INNER JOIN CLIENTIDs i ON i.CLIENTID = c.CLIENTID
ORDER BY NAME
I think you just want:
SELECT DISTINCT i.CLIENTID
FROM LCMINV i
WHERE i.CLIENTID IN (SELECT a.CLIENTID
FROM LAAPPL a JOIN
LACOBORW c
ON c.APPLNO = a.APPLNO
WHERE a.STATUS IN ('PEN', 'REC')
);
I'm not really sure why you are joining in LACOBORW. You can probably remove that logic.
I would recommend left joins to get the data you need. Unfortunately, your question 3 does not seem to fit your model because your LCMINV table does not seem to have an id that matches to another table and I cannot understand where LCMINV.CLIENTID should be related to in another context. This works in MSSQL - cannot be sure about Oracle.
--this statement will give you all the names that have a status in the where clause
--you will receive NULL if there is no Co-BorrowerID
select
a.name
, b.applno
, b.status
, c.coborwid
from
lcclient a left join
laapl b on a.clientid = b.clientid left join
lacoborw c on b.applno = c.applno
where
b.status in ('PEN', 'REC')
order by
a.name

How to combine these 2 SQL query results?

1st Query:
Select O.TrainDate, O.RailHead, O.Position as Pos,
O.Iso, C.CustomerName + ' (' + O.Customer + ')' as Customer,
P.ProductName + ' ('+O.Product +')' as Product,
O.Target, O.Docket, o.Gross, o.Tare ,o.Net, o.ConNote, o.Description
from IsoOrders O, Customer C, Product P
where o.Customer = c.Customer
and p.Product = o.Product
and o.Traindate >= '12-14-2016'
and o.Iso = '040'
2nd Query:
select top 1 isodeldocket, product from trans where container = '040'
order by despatchdatetime desc
The last query's result was to be added as the last 2 columns of the 1st query.
Solving the problem in your query
You can do it like this:
select
O.TrainDate, O.RailHead, O.Position as Pos, O.Iso,
C.CustomerName + ' (' + O.Customer + ')' as Customer,
P.ProductName + ' ('+ O.Product +')' as Product,
O.Target, O.Docket, O.Gross, O.Tare, O.Net, O.ConNote, O.Description,
-- Added these columns
T.isodeldocket,
T.product
from
IsoOrders O,
Customer C,
Product P,
-- Added this derived table
(select top 1 isodeldocket, product
from trans
where container = '040'
order by despatchdatetime desc) T
where O.Customer = C.Customer and P.Product = O.Product
and O.Traindate >= '12-14-2016'
and O.Iso = '040'
Improving the query by using ANSI JOIN syntax
While you're refactoring this query, why not move to ANSI JOIN, which greatly simplifies readability and clearly shows the intent / difference between (INNER) JOIN and CROSS JOIN:
select
O.TrainDate, O.RailHead, O.Position as Pos, O.Iso,
C.CustomerName + ' (' + O.Customer + ')' as Customer,
P.ProductName + ' ('+ O.Product +')' as Product,
O.Target, O.Docket, O.Gross, O.Tare, O.Net, O.ConNote, O.Description,
T.isodeldocket,
T.product
from IsoOrders O
join Customer C on O.Customer = C.Customer
join Product P on P.Product = O.Product
-- CROSS JOIN more explicitly reveals your intention than a comma-separated table list
cross join (
select top 1 isodeldocket, product
from trans
where container = '040'
order by despatchdatetime desc
) T
where O.Traindate >= '12-14-2016'
and O.Iso = '040'
Correlating the "outer" O.Iso with the "inner" trans.container value
From your comments, I take that you want to take this one step further and avoid duplicating the '040' "parameter". This can be done in SQL Server using APPLY:
select
O.TrainDate, O.RailHead, O.Position as Pos, O.Iso,
C.CustomerName + ' (' + O.Customer + ')' as Customer,
P.ProductName + ' ('+ O.Product +')' as Product,
O.Target, O.Docket, O.Gross, O.Tare, O.Net, O.ConNote, O.Description,
T.isodeldocket,
T.product
from IsoOrders O
join Customer C on O.Customer = C.Customer
join Product P on P.Product = O.Product
-- Use CROSS APPLY rather than CROSS JOIN
cross apply (
select top 1 isodeldocket, product
from trans
where container = O.Iso -- Now, you can access "outer" columns
order by despatchdatetime desc
) T
where O.Traindate >= '12-14-2016'
and O.Iso = '040'

Issue with Order By with FOR XML in T-sql (The ORDER BY clause is invalid in views, inline functions, derived tables)

select a.Hall, a.Title,
STUFF((SELECT ', ' + '[' + CONVERT(varchar(2),DATEPART(Hour, b.StartFilm))
+ ':' + CONVERT(varchar(2),DATEPART(Minute, b.StartFilm))
+ ' ' + CONVERT(varchar(2),DATEPART(Hour, b.EndTime))
+ ':' + CONVERT(varchar(2),DATEPART(Minute, b.EndTime))
+ ']'
FROM (select c.Name as Hall, b.Title,
Convert(time,a.StartFilmTime) as StartFilm,
Convert(time,a.EndFilmTime) as EndTime
from FilmSchedule a
left join Film b on a.FilmId = b.Id
left join Room c on a.RoomId = c.Id
where a.ApproveStatus = 1 and a.Status = 1
and CONVERT(date, a.StartFilmTime) = '05-06-2015'
) b
Where a.Hall = b.Hall and a.Title = b.Title
FOR XML PATH('')),1,1,'') As ShowTime
from (select c.Name as Hall, b.Title,
Convert(time,a.StartFilmTime) as StartFilm,
Convert(time,a.EndFilmTime) as EndTime
from FilmSchedule a
left join Film b on a.FilmId = b.Id
left join Room c on a.RoomId = c.Id
where a.ApproveStatus = 1 and a.Status = 1
and CONVERT(date, a.StartFilmTime) = '05-06-2015'
Order by a.StartFilmTime
) a
group by a.Hall, a.Title
I get the error:
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.
Help please! (I have used FOR XML?)
Although your query does use FOR XML (for the GROUP_CONCAT workaround), you are applying the order by outside of the derived table that uses FOR XML, hence the error.
Given that you aren't including start date directly in the final select (although you are composing it as part of the STUFF ShowTime column), you also can't ORDER BY StartFilm in the final GROUP BY either, as the column would otherwise need to be included in the GROUP BY or as an aggregated column.
What you can do is move the ORDER BY into the STUFF and then order by the derived column ShowTime (since your query only runs for one given day, and StartFilmTime is the first part of the STUFFED composed column).
At the same time, I would DRY up the repetition on the derived table with a CTE:
WITH cteFiltered AS
(select c.Name as Hall, b.Title,
Convert(time,a.StartFilmTime) as StartFilm,
Convert(time,a.EndFilmTime) as EndTime
from FilmSchedule a
left join Film b on a.FilmId = b.Id
left join Room c on a.RoomId = c.Id
where a.ApproveStatus = 1 and a.Status = 1
and CONVERT(date, a.StartFilmTime) = '05-06-2015'
)
select
a.Hall,
a.Title,
STUFF((SELECT ', ' + '[' + CONVERT(varchar(2),DATEPART(Hour, b.StartFilm))
+ ':' + CONVERT(varchar(2),DATEPART(Minute, b.StartFilm))
+ ' ' + CONVERT(varchar(2),DATEPART(Hour, b.EndTime))
+ ':' + CONVERT(varchar(2),DATEPART(Minute, b.EndTime))
+ ']'
FROM
cteFiltered b
Where
a.Hall = b.Hall and a.Title = b.Title
order by b.StartFilm -- ***
FOR XML PATH('')),1,1,'') As ShowTime
from
cteFiltered a
group by a.Hall, a.Title
order by ShowTime; -- *** Hour is first (assuming 24H format) and only one day

How to pass function a output of select query

I am working on following query
Select p.* from Product Inner Join Product_Category_Maping pcm on P.Id=Pcm.ProductId
where
dbo.[CHECKCHARINDEX](#Keyword,p.Name+' '+
#CatName=(Select C.Name from category C where C.Id=Pcm.CategoryId))=1
I want to pass both product and category name to function for some check . How can I do it
I suggest you to move the select to a join. Makes it easier and clearer.
select p.*
from product p
join product_category_maping pcm
on p.id = pcm.productid
join category c
on c.id = pcm.categoryid
where dbo.[checkcharindex](#keyword, p.name + ' ' + c.name) = 1
Also, I think you forgot to give the product table the alias p.

How can I use Sql to Order By This Statement?

How can I order the list 'widgets_spec by number of widgets?
select distinct
m.p_c_id
,(select distinct '<li>' +convert(varchar,widgets) + '<br> '
from dbo.spec_master m2
where m.p_c_id = m2.p_c_id and widgets is not null
for xml path(''), type).value('.[1]', 'nvarchar(max)'
) as widgets_spec
from dbo.spec_master m
inner join dbo.ProductVaration pv on pv.p_c_id = m.p_c_id
inner join dbo.Varation v on v.varation_id = pv.varation_type_id
where v.varation_id = 4
group by m.p_c_id
Right now output looks like:
<li>10<br> <li>12<br> <li>15<br> <li>8<br>
When I want it to look like:
<li>8<br> <li>10<br> <li>12<br> <li>15<br>
Thanks for your help.
EDIT: I'm trying to order the internal select statement that concatenates the values.
You do not need both Distinct and Group By. You should use one or the other. In this case, I believe you have to use Group By for it to work.
Select m.p_c_id
, (
Select '<li>' + Cast( m2.num_of_lights As varchar(10)) + '<br /> '
From dbo.spec_master As m2
Where m.p_c_id = m2.p_c_id
And m2.num_of_lights Is Not Null
Group By m2.num_of_lights
Order By m2.num_of_lights
For Xml Path(''), type).value('.[1]', 'nvarchar(max)'
) As numLights_spec
From dbo.spec_master As m
Inner Join dbo.ProductVaration As pv
On pv.p_c_id = m.p_c_id
Inner Join dbo.Varation As v
On v.varation_id = pv.varation_type_id
Where v.varation_id = 4
Group by m.p_c_id
select distinct
m.p_c_id
,(select distinct '<li>' +convert(varchar,num_of_lights) + '<br> '
from dbo.spec_master m2
where m.p_c_id = m2.p_c_id and num_of_lights is not null
ORDER BY convert(varchar,num_of_lights)
) as numLights_spec
from dbo.spec_master m
inner join dbo.ProductVaration pv on pv.p_c_id = m.p_c_id
inner join dbo.Varation v on v.varation_id = pv.varation_type_id
where v.varation_id = 4
group by m.p_c_id
) As SubA
Some of the other answers here won't work, since ordering by the now-varchar num_of_lights will put '8' after '15' as is happening now. You want to order the numLights numerically, which isn't going to happen with those html tags around them. You can add a subselect to your subselect so that you order them, then select them with the tags around them. Example (not tested):
SELECT * FROM (
select distinct
m.p_c_id
,(select distinct '<li>' +convert(varchar,num_of_lights) + '<br> '
from (select distinct p_c_id, num_of_lights from dbo.spec_master order by num_of_lights) m2
where m.p_c_id = m2.p_c_id and num_of_lights is not null
for xml path(''), type).value('.[1]', 'nvarchar(max)'
) as numLights_spec
from dbo.spec_master m
inner join dbo.ProductVaration pv on pv.p_c_id = m.p_c_id
inner join dbo.Varation v on v.varation_id = pv.varation_type_id
where v.varation_id = 4
group by m.p_c_id
Personally, I'd just add the html tags in whatever back-end code is getting the result of the query.