I have 2 SQL statements. I need one to select its results based on the selection from the other. The tables are not related. The only relation will be the results from the initial selection.
Initial selection...
SELECT
relatedseries
FROM RelatedItems
WHERE series = #getseries
ORDER BY ID
Selection based on results of above selection...
SELECT
picid,
description
FROM product_series
WHERE newseries = relatedseries
I know this is incorrect, partly because I have tried it, but mostly I am sure the syntax is incorrect. But for example....
SELECT
picid,
description
FROM product_series
WHERE newseries = (
SELECT
relatedseries
FROM RelatedItems
WHERE series = #getseries ORDER BY ID
)
How can I rewrite this to work and still achieve the results I require?
Use a JOIN rather than 2 queries.
SELECT p.picid, p.description, r.relatedseries
FROM product_series p
INNER JOIN RelatedItems r ON p.newseries = r.relatedseries
AND r.series = #getseries
Get the data that you need in as few passes as you can manage.
NOTE: This may give you duplicates of p.picid and p.description if you have multiple records in RelatedItems that match up to product_series.
You can use IN for multiple results from your first query
SELECT picid,
description
FROM product_series
WHERE newseries IN (SELECT relatedseries
FROM RelatedItems
WHERE series = #getseries
)
ORDER BY picid;
You can do it with exists
SELECT picid, description FROM product_series WHERE newseries = relatedseries
Where
exists (SELECT 1FROM RelatedItems WHERE series = #getseries ORDER BY ID)
I think your best bet would be to use join
SELECT picid, description
FROM product_series
INNER JOIN RelatedItems ON newseries = relatedseries;
here is an example sql join
All's good aside for two issues, though the error message(s) should have been simple to understand.
You cannot have an ORDER BY clause in the nested SELECT statement. Try removing that piece.
Additionally, if your nested query is returning more than 1 result, you cannot have an equals before that query - replace that with IN.
Corrected query
SELECT
picid,
description
FROM product_series
WHERE newseries IN (
SELECT
relatedseries
FROM RelatedItems
WHERE series = #getseries
--ORDER BY ID
)
SELECT NS.picid, NS.description
FROM product_series NS,
(SELECT relatedseries FROM RelatedItems WHERE
series = #getseries) RI
WHERE NS.newseries = RI.relatedseries
ORDER BY NS.picid ASC
Related
I have two tables
InvoiceHead(InvoiceID,RetailerID,ExecutiveID,InvoiceDate)
InvoiceItem(InvoiceID,ItemID,Qty,Contract,Amount).
I want to get the sales qty grouped by ExecutiveId and ItemId.
I tried Following query but it doesn't give expected outcome.Grouping by both columns is not happening.As in the following screenshot the Same executive and item is not added together.they are in two rows
Screenshot of the result
SELECT
SecItem.ItemID
,SecHead.ExecutiveID
,sum(SecItem.QTY) AS Total_Qty
FROM Secondary_Sales.dbo.InvoiceHead AS SecHead
INNER JOIN Secondary_Sales.dbo.InvoiceItem AS SecItem
ON SecHead.InvoiceID = SecItem.Invoice_ID
GROUP BY
SecItem.ItemID
,SecHead.ExecutiveID
This query works in mysql and gives expected result but the same query doesn't work in msqlserver
The query as given should work, SQL Server does not implement things like this differently from MySql. I suspect this is a data issue. I suspect that the data for some of the IDs has trailing spaces. "GALSR02" does not equal "GALSR02 ". Try this query to see if that is the case:
SELECT
RTRIM(SecItem.ItemID)
,RTRIM(SecHead.ExecutiveID)
,sum(SecItem.QTY) AS Total_Qty
FROM Secondary_Sales.dbo.InvoiceHead AS SecHead
INNER JOIN Secondary_Sales.dbo.InvoiceItem AS SecItem
ON RTRIM(SecHead.InvoiceID) = RTRIM(SecItem.Invoice_ID)
GROUP BY
RTRIM(SecItem.ItemID)
,RTRIM(SecHead.ExecutiveID)
If that works then you will want to run this to correct the data and investigate how the white space got there in the first place. Once the data is corrected you can revert to your original query.
UPDATE Secondary_Sales.dbo.InvoiceHead
SET ExecutiveID = RTRIM(ExecutiveID), InvoiceID = RTRIM(InvoiceID)
UPDATE Secondary_Sales.dbo.InvoiceItem
SET ItemID = RTRIM(ItemID), Invoice_ID = RTRIM(Invoice_ID)
Try with the below query..
;with cte_1
as
(SELECT InvoiceID,ItemID,SUM(QTY) OVER (partition by ItemID order by ItemID)New_QTY
FROM InvoiceItem )
SELECT c.ItemID,h.ExecutiveID,sum(c.New_QTY) AS Total_Qty
FROM Secondary_Sales.dbo.InvoiceHead h
JOIN cte_1 c
on h.InvoiceID=c.InvoiceID
GROUP BY h.ExecutiveID,c.ItemID
if you wanted to pull the sum based on executiveId and item id, use the below query.
;with cte_1
as
(SELECT c.ItemID,h.ExecutiveID
,ROW_NUMBER() OVER(partition by h.ExecutiveID,c.ItemID order by h.ExecutiveID,c.ItemID) RNO
,SUM(QTY) OVER (partition by h.ExecutiveID,c.ItemID order by h.ExecutiveID,c.ItemID) AS Total_Qty
FROM Secondary_Sales.dbo.InvoiceHead h
JOIN econdary_Sales.dbo.InvoiceItem c
on h.InvoiceID=c.InvoiceID)
SELECT ExecutiveID,ItemID,Total_Qty
FROM cte_1
WHERE RNO=1
Try with below query hope it helps .
SELECT SecItem.ItemID,SecHead.ExecutiveID,sum(SecItem.QTY) AS Total_Qty
FROM Secondary_Sales.dbo.InvoiceHead AS SecHead
INNER JOIN Secondary_Sales.dbo.InvoiceItem AS SecItem ON SecHead.InvoiceID = SecItem.Invoice_ID
GROUP BY SecItem.ItemID,SecHead.ExecutiveID,SecItem.QTY
I have an update query that works fine, but it is way too slow and takes over 2 minutes to complete. Is there another way I can write this query to speed it up? Here is my code thanks:
UPDATE #tmpIMDS
SET
ModelFileName = b.ModelFileName,
SendEMail = b.SendEMail
FROM
(
SELECT DISTINCT
IMDSConversionReportData.ModelNumber,
ModelFileName,
'Send Email' AS SendEmail
FROM
IMDSConversionReportData,
(
SELECT DISTINCT
ModelNumber,
Max(DateAdded) AS DateAdded
FROM
IMDSConversionReportData
GROUP BY
ModelNumber) a
WHERE
IMDSConversionReportData.ModelNumber = a.ModelNumber
AND IMDSConversionReportData.DateAdded = a.DateAdded
) b
WHERE ModelID = b.ModelNumber
Instead of hitting IMDSConversionReportData table twice to get the maximum DateAdded per ModelNumber you can generate row_number to identify maximum DateAdded per ModelNumbercolumn.
Also remove distinct when you are selecting only one non aggregate column with group by which is meaningless
Try this
;WITH cte
AS (SELECT *,
'Send Email' AS SendEmail,
Row_number()OVER(partition BY ModelNumber ORDER BY DateAdded DESC) AS rn
FROM IMDSConversionReportData)
UPDATE t
SET ModelFileName = c.ModelFileName,
SendEMail = c.SendEMail
FROM #tmpIMDS t
INNER JOIN cte c
ON t.ModelID = c.ModelNumber
Where Rn = 1
Note : Always use proper INNER JOIN syntax to join two tables instead of Old style comma separated join. We always find INNER Join syntax is more readable. Keep the filters alone in Where clause
I am getting the following error when I am trying to execute my SQL SELECT statement
Could not execute statement.
Correllation name 'contact' not found
SQLCODE=-142, ODBC 3 State"42S02"
Line 1, Column 1
My code is as follows
Select forename, surname, email, quotedate
From ( SELECT *, ROW_NUMBER() OVER (PARTITION BY tblQuote.contno ORDER BY quoteno DESC) AS rn
FROM dba.quotehdr as tblQuote left join dba.contact as tblContact on tblQuote.contno = tblContact.contno)q
where rn = 1 and quotedate <=today()-720 and emailbounced = 0 and email is not null and dba.contact.statusflag = 'A'
order by quotedate desc
This error only happended when I added in
dba.contact.statusflag = 'A'
I have tried this as
tblContact.statusflag = 'A'
and I get the same error!
Any suggestions?
(What about q.statusflag = 'A' , as it seems you are using q as an Alias.) This original answer is not correct, amended to:
#Shannon Severance is correct in his comment. You are trying to use the Where clause on the outer query - which does not contain any fields from the contact table. Let me tidy your query to help you see your subquery (q) - as:
Select
forename
,surname
,email
, quotedate
From
(
SELECT
*
, ROW_NUMBER() OVER (PARTITION BY tblQuote.contno ORDER BY quoteno DESC) AS rn
FROM dba.quotehdr as tblQuote
left join dba.contact as tblContact on tblQuote.contno = tblContact.contno
) q
left join dba.contact as tblContact on q.contno = tblContact.contno
where rn = 1
and quotedate <=today()-720
and emailbounced = 0
and email is not null
and tblContact.statusflag = 'A' -- Now sourced from last left join
order by quotedate desc
You will need another LEFT JOIN on the dba.contact table to be able to access this field (ADDED NOW as an example).
Also, depending on your database engine - if your field is duplicated in both tables, the SELECT * in a subquery may eject those fields, or rename them, or throw an error. Run your inner subquery by itself and see what it produces, or use explicit field name instead of *
(I still really think your * in the subquery is causing the error and also the confusion. Remove it and replace with table.field names - this will help you understand what is going wrong ...Otherwise your query logic is pretty fine, and adding the extra left join that I suggest is overkill)
I'm trying to do a full text search but am having issues implementing it. I have an Object which can have multiple "Titles". I have a stored procedure that takes a language and country and returns a normalized result with the best "Title" based on the localization.
What I want to be able to do is perform a full text search on the "Title" table that will search all rows but then only return the normalized, default title.
To get the normalized result I have something like
NORMALIZING STATEMENT
SELECT MainObject.*, Title.Name, Description.Name
FROM MainObject
OUTER APPLY
(
SELECT TOP 1 Title.Name
FROM Title
WHERE Title.MainObject_ID = MainObject.ID AND Title.Language = #language
ORDER BY Title.[Default] DESC
)
OUTER APPLY
(
SELECT TOP 1 Description.Name
FROM Description
WHERE Description.MainObject_ID = MainObject.ID AND Description.Language = #language
ORDER BY Description.[Default] DESC
)
Say an object has 3 titles, Alpha, Bravo and Charlie with Alpha being the default Title.
I want to be able to do a full text search for Bravo. The object that Bravo is linked to is found but is normalized with the above statement. I.E search for "Bravo" and "Alpha" is returned as Title.Name.
The only way I can think of doing this is to first perform the Search such as
FULL-TEXT STATEMENT
SELECT Title.MainObject_ID, MAX(KEY_TBL.RANK)
FROM Title
INNER JOIN
FREETEXTTABLE(Title,Name,'Some Search') AS KEY_TBL
ON Title.ID = KEY_TBL.[KEY]
WHERE KEY_TBL.RANK >= 50
GROUP BY Title.MainObjectID, KEY_TBL.RANK
ORDER BY KEY_TBL.RANK DESC
This returns a list of MainObjects. I can then run these through the top statement. Is there a way to combine the two in a more elegant manor to thus get better performance?
UPDATE:
I can perform a WHERE IN on the full text statement but I'm not sure how to get the RANK back out to the main statement so I can have final output ordered by descending RANK.
NORMALIZING STATEMENT
WHERE MainObject.ID IN
(
FULL-TEXT STATEMENT
)
I got there in the end.... I started with the full text search on the "title" Table, then joined this back to the MainObject Table and carried on from there. I have to group by everything I want to select.
SELECT MainObject.ID, MAX(KEY_TBL.RANK) AS IndexRank, MainObject.OtherColumns, Title.Name, Description.Name
FROM Title AS FT_TBL
INNER JOIN
FREETEXTTABLE(Title,Name,'Some Search') AS KEY_TBL
ON FT_TBL.ID = KEY_TBL.[KEY]
INNER JOIN
MainObject ON FT_TBL.MainObjectID = MainObject.ID
OUTER APPLY
(
SELECT TOP 1 Title.Name
FROM Title
WHERE Title.MainObject_ID = MainObject.ID AND Title.Language = #language
ORDER BY Title.[Default] DESC
)
OUTER APPLY
(
SELECT TOP 1 Description.Name
FROM Description
WHERE Description.MainObject_ID = MainObject.ID AND Description.Language = #language
ORDER BY Description.[Default] DESC
)
GROUP BY MainObject.ID, KEY_TBL.RANK, MainObject.OtherColumns, Title.Name, Description.Name
ORDER BY IndexRank desc
I have a table that represents an Object. It has many columns but also fields that require language support.
For simplicity let's say I have 3 tables:
MainObjectTable
LanguageDependantField1
LanguageDependantField2.
MainObjectTable has a PK int called ID, and both LanguageDependantTables have a foreign key link back to the MainObjectTable along with a language code and the date they were added.
I've created a stored procedure that accepts the MainObjectTable ID and a Language. It will return a single row containing the most recent items from the language tables. The select statement looks like
SELECT
MainObjectTable.VariousColumns,
LanguageDependantField1.Description,
LanguageDependantField2.SomeOtherText
FROM
MainObjectTable
OUTER APPLY
(SELECT TOP 1 LanguageDependantField1.Description
FROM LanguageDependantField1
WHERE LanguageDependantField1.MainObjectTable_ID = MainObjectTable.ID
AND LanguageDependantField1.Language_ID = #language
ORDER BY
LanguageDependantField1.[Default], LanguageDependantField1.CreatedDate DESC) LanguageDependantField1
OUTER APPLY
(SELECT TOP 1 LanguageDependantField2.SomeOtherText
FROM LanguageDependantField2
WHERE LanguageDependantField2.MainObjectTable_ID = MainObjectTable.ID
AND LanguageDependantField2.Language_ID = #language
ORDER BY
LanguageDependantField2.[Default] DESC, LanguageDependantField2.CreatedDate DESC) LanguageDependantField2
WHERE
MainObjectTable.ID = #MainObjectTableID
What I want to add is the ability to fallback to a default language if a row isn't found in the specified language. Let's say we use "German" as the selected language. Is it possible to return an English row from LanguageDependantField1 if the German does not exist presuming we have #fallbackLanguageID
Also am I right to use OUTER APPLY in this scenario or should I be using JOIN?
Many thanks for your help.
Try this:
SELECT MainObjectTable.VariousColumns,
COALESCE(PrefLang.Description,Fallback.Description,'Not Found Desc')
as Description,
COALESCE(PrefLang.SomeOtherText,FallBack.SomeOtherText,'Not found')
as SomeOtherText
FROM MainObjectTable
LEFT JOIN
(SELECT TOP 1 pl.Description,pl.SomeOtherText
FROM LanguageDependantField1 pl
WHERE pl.MainObjectTable_ID = MainObjectTable.ID
AND pl.Language_ID = #language
ORDER BY
pl.[Default], pl.CreatedDate DESC)
PrefLang ON 1=1
LEFT JOIN
(SELECT TOP 1 fb.Description,fb.SomeOtherText
FROM LanguageDependantField1 fb
WHERE fb.MainObjectTable_ID = MainObjectTable.ID
AND fb.Language_ID = #fallbackLanguageID
ORDER BY
fb.[Default], fb.CreatedDate DESC)
Fallback ON 1=1
WHERE
MainObjectTable.ID = #MainObjectTableID
Basically, make two queries, one to the preferred language and one to English (Default). Use the LEFT JOIN, so if the first one isn't found, the second query is used...
I don't have your actual tables, so there might be a syntax error in above, but hope it gives you the concept you want to try...
Yes, the use of Outer Apply is correct if you want to correlate the MainObjectTable table rows to the inner queries. You cannot use Joins with references in the derived table to the outer table. If you wanted to use Joins, you would need to include the joining column(s) and in this case pre-filter the results. Here is what that might look like:
With RankedLanguages As
(
Select LDF1.MainObjectTable_ID, LDF1.Language_ID, LDF1.Description, LDF1.SomeOtherText, ...
, Row_Number() Over ( Partition By LDF1.MainObjectTable_ID, LDF1.Language_ID
Order By LDF1.[Default] Desc, LDF1.CreatedDate Desc ) As Rnk
From LanguageDependantField1 As LDF1
Where LDF1.Language_ID In( #languageId, #defaultLanguageId )
)
Select M.VariousColumns
, Coalesce( SpecificLDF.Description, DefaultLDF.Description ) As Description
, Coalesce( SpecificLDF.SomeOtherText, DefaultLDF.SomeOtherText ) As SomeOtherText
, ...
From MainObjectTable As M
Left Join RankedLanguages As SpecificLDF
On SpecificLDF.MainObjectTable_ID = M.ID
And SpecifcLDF.Language_ID = #languageId
And SpecifcLDF.Rnk = 1
Left Join RankedLanguages As DefaultLDF
On DefaultLDF.MainObjectTable_ID = M.ID
And DefaultLDF.Language_ID = #defaultLanguageId
And DefaultLDF.Rnk = 1
Where M.ID = #MainObjectTableID