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
Related
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
I have a page that shows all the Application Types for a product.
I have a view dbo.View_Products_by_Application and a table dbo.tblApplications.
dbo.tblApplicaitons has two fields, ApplicationName & ApplicationID
dbo.View_Products_By_Application has all the product information. What I want to do is create a record set that uses dbo.tblApplications and returns only the ApplicationNames where that application Name also Appears in the dbo.View_Produts_by_Application view. And in the recordset returned, I only want each ApplicationName to appear once.
The two items I need in my record set are ApplicationName and ApplicationID.
SELECT *
FROM [WebV2].[dbo].[View_Products_By_Application]
INNER JOIN dbo.tblApplications
ON dbo.View_Products_By_Application.ApplicationName = dbo.tblApplications.ApplicationName
Order BY dbo.View_Products_By_Application.ApplicationName ASC
I didn't quite get what you have there but I think what you are looking for is something like this.
SELECT [WebV2].[dbo].[View_Products_By_Application].*, dbo.tblApplications.*, ROW_NUMBER() OVER (PARTITION BY dbo.View_Products_By_Application.ApplicationName Order BY dbo.View_Products_By_Application.ApplicationName ASC)
As rownum
FROM [WebV2].[dbo].[View_Products_By_Application]
INNER JOIN dbo.tblApplications ON dbo.View_Products_By_Application.ApplicationName = dbo.tblApplications.ApplicationName
Where rownum= 1
This will return to you the first by ApplicationName, I believe.
http://technet.microsoft.com/en-us/library/ms186734.aspx
You could use EXISTS:
SELECT app.ApplicationName, app.ApplicationID
FROM dbo.tblApplications app
WHERE EXISTS
(
SELECT *
FROM [WebV2].[dbo].[View_Products_By_Application] vw
WHERE vw.ApplicationName = app.ApplicationName
)
ORDER BY app.ApplicationName;
I think you can just do a select distinct to get what you want:
SELECT distinct a.ApplicationName, a.ApplicationId
FROM [WebV2].[dbo].[View_Products_By_Application] pba
INNER JOIN dbo.tblApplications a
ON pba.ApplicationName = a.ApplicationName
Order BY a.ApplicationName ASC
I also added table aliases to make your query more readable.
I have a SQL Server 2005 query that I'm trying to assemble right now but I am having some difficulties.
I have a group by clause based on 5 columns: Project, Area, Name, User, Engineer.
Engineer is coming from another table and is a one to many relationship
WITH TempCTE
AS (
SELECT htce.HardwareProjectID AS ProjectId
,area.AreaId AS Area
,hs.NAME AS 'Status'
,COUNT(*) AS Amount
,MAX(htce.DateEdited) AS DateModified
,UserEditing AS LastModifiedName
,Engineer
,ROW_NUMBER() OVER (
PARTITION BY htce.HardwareProjectID
,area.AreaId
,hs.NAME
,htce.UserEditing ORDER BY htce.HardwareProjectID
,Engineer DESC
) AS row
FROM HardwareTestCase_Execution AS htce
INNER JOIN HardwareTestCase AS htc ON htce.HardwareTestCaseID = htc.HardwareTestCaseID
INNER JOIN HardwareTestGroup AS htg ON htc.HardwareTestGroupID = htg.HardwareTestGroupId
INNER JOIN Block AS b ON b.BlockId = htg.BlockId
INNER JOIN Area ON b.AreaId = Area.AreaId
INNER JOIN HardwareStatus AS hs ON htce.HardwareStatusID = hs.HardwareStatusId
INNER JOIN j_Project_Testcase AS jptc ON htce.HardwareProjectID = jptc.HardwareProjectId AND htce.HardwareTestCaseID = jptc.TestcaseId
WHERE (htce.DateEdited > #LastDateModified)
GROUP BY htce.HardwareProjectID
,area.AreaId
,hs.NAME
,htce.UserEditing
,jptc.Engineer
)
The gist of what I want is to be able to deal with empty Engineer columns. I don't want this column to have a blank second entry (where row=2).
What I want to do:
Group the items with "row" value of 1 & 2 together.
Select the Engineer that isn't empty.
Do not deselect engineers where there is not a matching row=2.
I've tried a series of joins to try and make things work. No luck so far.
Use j_Project_Testcase PIVOT( MAX(Engineer) for Row in ( [1], [2] ) then select ISNULL( [1],[2]) to select the Engineer value
I can give you a more robust example if you set up a SQL fiddle
Try reading this: PIVOT and UNPIVOT
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
When using get_results($sql, ARRAY_A) to query wp_comments, there is a comment in the result.
The sql is below
SELECT comment_ID,comment_parent,comment_author,comment_author_url,comment_author_email,comment_content,comment_date,comment_type,ck_rating_up,ck_rating_down
FROM $wpdb->comments
LEFT JOIN $comment_rating_table
ON ($wpdb->comments.comment_ID = $comment_rating_table.ck_comment_id)
WHERE comment_post_ID = $post_id AND comment_approved = 1
order by comment_id ASC
How to avoid duplicate comments?
The question being asked is, how to get the results containing no duplicates. Since nothing was mentioned as to which field, I will assume it's the main comments field which is of type text, which means you can't use DISTINCT on it.
However since this field contains comments and not novels worth of text, then really there should be no reason for a comment field to be of type text at all. So what needs to happen is for the field to be convert from type text to type varchar, and use (max) as the field size.
The varchar(max) field can hold 64k of data, which is about 10,000-20,000 words depending on the alphabet used. This should be more than enough room to hold a 40 page essay or a single comment.
As for the SQL:
SELECT comment_ID,comment_parent,comment_author,comment_author_url,comment_author_email,comment_content,comment_date,comment_type,ck_rating_up,ck_rating_down
FROM $wpdb->comments
LEFT JOIN $comment_rating_table
ON ($wpdb->comments.comment_ID = $comment_rating_table.ck_comment_id)
WHERE comment_post_ID = $post_id
AND comment_approved = 1
AND comment_ID IN (SELECT DISTINCT( comment_content ) FROM $wpdb->comments
LEFT JOIN $comment_rating_table
ON ($wpdb->comments.comment_ID = $comment_rating_table.ck_comment_id)
WHERE comment_post_ID = $post_id AND comment_approved = 1)
order by comment_id ASC
A faster option is instead of DISTINCT to use GROUP BY comment_content , and then selecting either the MIN or MAX
SELECT comment_ID,comment_parent,comment_author,comment_author_url,comment_author_email,comment_content,comment_date,comment_type,ck_rating_up,ck_rating_down
FROM $wpdb->comments
LEFT JOIN $comment_rating_table(
SELECT MAX(comment_id) AS id FROM comment GROUP BY comment_content
maxid ON ($wpdb->comments.comment_ID = $comment_rating_table.ck_comment_id) WHERE comment_post_ID = $post_id AND comment_approved = 1) order by comment_id ASC