TSQL: query with optional join - sql

I have a search ui with 3 all optional search criteria.
2 of them are simple criteria for a where statement, that I should be able to solve with this: Stored Procedure with optional "WHERE" parameters.
The last criterion is using full text search where I join the result from ContainsTable. Is there some trick I can use to put everything in one stored procedure? Or should I make two queries, one with the full text search and one without?
Edited: I should have put my query here as well, sorry here it is
select Table1.* from Table1
join
(
select [Key], SUM(Rank) as Rank from
(
SELECT [Key], Rank*3 as Rank FROM Table1ShortSearch(#Keywords) union all
SELECT [Key], Rank*2 as Rank FROM Table1LongSearch(#Keywords)
) as RankingTbl
group by [Key]
) as r
on Table1.Id = r.[Key]
where ( #Status_Id Is Null Or Status_Id = #Status_Id )
order by r.Rank Desc
Thanks.

you can put everything in one stored procedure and have an IF statement - http://msdn.microsoft.com/en-us/library/ms182717.aspx

Original Answer:
You can use an EXISTS function like so:
Select ..
From ..
Where ( #Status_Id Is Null Or Status_Id = #Status_Id )
And (#Date Is Null Or [Date] = #Date )
And (#Criteria Is Null Or Exists(
Select 1
From ContainsTable(TableName, Column1,...,ColumnN, #Criteria..) As SearchTable1
Where SearchTable1.PK = OuterTable.PK
) )
After question revision:
The revised query is of a completely different nature than the original query. In the original query, you simply wanted to return results from Table1 and additionally filter those results if #Keywords was not null. In this query, you are outputting to the SELECT clause the freetext ranking. What would display for the ranking if #Keywords was passed as null?
If freetext ranking is not needed and you simply want to return results if either of the searches on #Keywords finds something, then you would do something like:
Select ...
From Table1
Where ( #Status_Id Is Null Or Status_Id = #Status_Id )
And ...
And (
#Keywords Is Null
Or Exists (
Select 1
From Table1ShortSearch(#Keywords) As T1
Where T1.Key = Table1.Key
)
Or Exists (
Select 1
From Table1LongSearch(#Keywords) As T2
Where T2.Key = Table1.Key
)
)
If you want to display the freetext ranking then your original query or perhaps a CTE would be the solution however you will need to use a Left Join to your subquery if you want to account for #Keywords being null. That would make your query read:
Select ...
From Table1
Left Join (
Select [Key], Sum(Rank) as Rank
From (
Select [Key], Rank*3 As Rank
From Table1ShortSearch(#Keywords)
Union All
Select [Key], Rank*2 As Rank
From Table1LongSearch(#Keywords)
) As RankingTbl
Group By [Key]
) as R
On R.[Key] = Table1.Id
Where ( #Status_Id Is Null Or Status_Id = #Status_Id )
And ...
And ( #Keywords Is Null Or R.Key Is Not Null )
Order By R.Rank Desc

Related

How to get last record from Master-Details tables

I have a table that has 3 columns.
create table myTable
(
ID int Primary key,
Detail_ID int references myTable(ID) null, -- reference to self
Master_Value varchar(50) -- references to master table
)
this table has the follow records:
insert into myTable select 100,null,'aaaa'
insert into myTable select 101,100,'aaaa'
insert into myTable select 102,101,'aaaa'
insert into myTable select 103,102,'aaaa' ---> last record
insert into myTable select 200,null,'bbbb'
insert into myTable select 201,200,'bbbb'
insert into myTable select 202,201,'bbbb' ---> last record
the records is saved In the form of relational with ID and Detail_ID columns.
I need to select the last record each Master_Value column. follow output:
lastRecordID Master_Value Path
202 bbbb 200=>201=>202
103 aaaa 100=>101=>102=>103
tips:
The records are not listed in order in the table.
I can not use the max(ID) keyword. beacuse data is not sorted.(may
be the id column updated manually.)
attempts:
I was able to Prepare follow query and is working well:
with Q as
(
select ID ,Detail_ID, Master_Value , 1 RowOrder, CAST(id as varchar(max)) [Path] from myTable where Detail_ID is null
union all
select R.id,R.Detail_ID , r.Master_Value , (q.RowOrder + 1) RowOrder , (q.[Path]+'=>'+CAST(r.id as varchar(max))) [Path] from myTable R inner join Q ON Q.ID=R.Detail_ID --where r.Dom_ID_RowType=1010
)
select * into #q from Q
select Master_Value, MAX(RowOrder) lastRecord into #temp from #Q group by Master_Value
select
q.ID lastRecordID,
q.Master_Value,
q.[Path]
from #temp t
join #q q on q.RowOrder = t.lastRecord
where
q.Master_Value = t.Master_Value
but I need to simple way (one select) and optimal method.
Can anyone help me?
One method uses a correlated subquery to get the last value (which is how I interpreted your question):
select t.*
from mytable t
where not exists (select 1
from mytable t2
where t2.master_value = t.master_value and
t2.id = t.detail_id
);
This returns rows that are not referred to by another row.
For the path, you need a recursive CTE:
with cte as (
select master_value, id as first_id, id as child_id, convert(varchar(max), id) as path, 1 as lev
from mytable t
where detail_id is null
union all
select cte.master_value, cte.first_id, t.id, concat(path, '->', t.id), lev + 1
from cte join
mytable t
on t.detail_id = cte.child_id and t.master_value = cte.master_value
)
select cte.*
from (select cte.*, max(lev) over (partition by master_value) as max_lev
from cte
) cte
where max_lev = lev
Here is a db<>fiddle.

SQL Having count logic

i need help on HAVING COUNT , i have a result set of data below:
CREATE TABLE #tmpTest1 (Code VARCHAR(50), Name VARCHAR(100))
INSERT INTO [#tmpTest1]
(
[Code],
[Name]
)
SELECT '160215-039','ROBIN'
UNION ALL SELECT '160215-039','ROBIN'
UNION ALL SELECT '160215-046','SENGAROB'
UNION ALL SELECT '160215-046','BABYPANGET'
UNION ALL SELECT '160215-045','JONG'
UNION ALL SELECT '160215-045','JAPZ'
UNION ALL SELECT '160215-044','AGNES'
UNION ALL SELECT '160215-044','AGNES'
UNION ALL SELECT '160215-041','BABYTOT'
UNION ALL SELECT '160215-041','BABYTOT'
UNION ALL SELECT '160215-041','BABYTOT'
i want to show only the rows that have the same code but different name , so in this case my expected result is below since those are have the same code but different name:
160215-045 JAPZ
160215-045 JONG
160215-046 BABYPANGET
160215-046 SENGAROB
but when i try to group the two columns then use the having count, below is my query:
SELECT [Code], [Name] FROM [#tmpTest1]
GROUP BY [Code], [Name] HAVING COUNT([Code]) > 1
It gives me wrong result below which have the rows that have the same code and name, it is the opposite of what i want.
160215-044 AGNES
160215-041 BABYTOT
160215-039 ROBIN
How can i get my expected output ?
Thanks in advance, any help would much appreciated.
I believe this query will give you the result you want, although your original question is a bit unclear.
SELECT t1.[Code], t1.[Name]
FROM [#tmpTest1] t1
INNER JOIN
(
SELECT [Code]
FROM [#tmpTest1]
GROUP BY [Code]
HAVING COUNT(DISTINCT [Name]) > 1
) t2
ON t1.[Code] = t2.[Code]
Follow the link below for a running demo:
SQLFiddle
If you want rows with the same code and name, then use window functions:
select t.*
from (select t.*, count(*) over (partition by code, name) as cnt
from #temptest1 t
) t
where cnt >= 2;
From your comment
if there is 1 different name for the codes , i want to show those
records for me to know that there is one differs to others..
This sounds like an exists query because you want to check if another row with the same code but different name exists.
select * from [#tmpTest1] t1
where exists (
select 1 from [#tmpTest] t2
where t2.code = t1.code
and t2.name <> t1.name
)

WHERE IN sql query

I need to find which items in WHERE IN clause do not exist in the database. in below example cc33 does not exist and I need the query to give back cc33.
how would I do that ?
SELECT id FROM tblList WHERE field1 IN ('aa11','bb22','cc33')
You need to put the values into a table rather than a list:
with list as (
select 'aa11' as val union all
select 'bb22' union all
select 'cc33'
)
select l.val
from list l left outer join
tbllist t
on l.val = t.field1
where t.field1 is null
For SQl-Server versions of 2008+, you can use a Table Value Constructor:
SELECT field1
FROM
( VALUES
('aa11'),('bb22'),('cc33')
) AS x (field1)
WHERE field1 NOT IN
( SELECT field1 FROM tblList ) ;
Tested at SQL-Fiddle

reuse table alias in another select

I have a sql statement:
select id from table1 t1, table t2
where.....
order by ( select count(owner_id) from t2) ASC;
What I want to do here is to select the id of the item whose owner has least number of items.
Is this possible? If not, what I can do to achieve to goal?
Thanks in advance!
You don't mention what SQL you're using but you can do this, or something similar, in PL ( and My I believe ); I'm assuming you're linking table 1 and 2 on id; I haven't ordered by the count(owner_id) alone as this will always be the same value. Obviously partition by whatever you want to get the correct count you're after.
select id
from ( select t1.id, t2.ct
from table1 t1
, ( select id, count(owner_id) over ( partition by id ) as ct
from table2 ) t2
where t1.id = t2.id
order by t2.ct ASC )
;

Wrap-around in SQL results ordering

If I have a table with items beginning with C, D, and J, is it in any way possible to arrange a query that orders these ascending, but starts with D, and ends with C wrapped around?
E.g. raw table = C,C,C,D,D,J
Desired result order = D,D,J,C,C,C
In ordinary SQL this is, not any functional language? I can't see how the desired order can be achieved without hard-coding, i.e. selecting each individual record in the desired order all union'd together.
You could introduce a custom sort key with a CASE statement.
Select Col1, Col2, Col3,
case left(Col3,1) when 'D' then 1
when 'J' then 2
when 'C' then 3
else 4
end as SortKey
from YourTable
order by SortKey, Col3
That does not seem like a regular order, so you won't be able to do it in a simple way. But you can do this:
(SELECT * FROM yourtable WHERE ID >=C ORDER BY ID) UNION (SELECT SELECT * FROM yourtable WHERE ID <C ORDER BY ID)
You can implement custom sort orders without hardcoding by using a table:
CREATE TABLE SortRules (
Prefix char(1) NOT NULL
,SortOrder int NOT NULL
)
Then join to the SortRules table:
SELECT *
FROM YourTable
LEFT JOIN SortRules
ON YourTable.YourColumn LIKE SortRules.Prefix + '%'
ORDER BY SortRules.SortOrder, YourTable.YourColumn
You can make SortOrder UNIQUE (although that's not required). You can also decide if you want missing ones (unmatched in the join) to be at the top or bottom:
SELECT *
FROM YourTable
LEFT JOIN SortRules
ON YourTable.YourColumn LIKE SortRules.Prefix + '%'
ORDER BY COALESCE(SortRules.SortOrder, 2147483647), YourTable.YourColumn
SELECT *
FROM YourTable
LEFT JOIN SortRules
ON YourTable.YourColumn LIKE SortRules.Prefix + '%'
ORDER BY COALESCE(SortRules.SortOrder, -2147483648), YourTable.YourColumn