Finding Where Clause Criteria in SQL Queries - sql

I have a Table which has 3 different Select queries.
e.g.
Staff
------------------------------------------------------------
ID Code Name Phone DOB Email Addr1 Addr2 Addr3
Query1
Select ID, Code, Phone From Staff Where Code = 'ABC'
Query2
Select ID, Code, Phone From Staff Where Name = 'ABCXYZ' And Code = 'B'
Query3
Select ID, Code, Phone From Staff Where Phone= '1234' And Email = 'a#b'
These 3 queries are there in 3 different stored procedure.
I want to find the names of Attributes which i have used in all my Where clauses. But want to Automate this as I have more than 100 tables to look for
Something like this
exec fxGetWhereColList ('Staff');
Result:
Code
Name
Phone
Email

I agree with all posts before, it's a complex case.
I post you a small idea to help you in your work.
If you can retrieve each execution plan, you can parse XML result and get the impacted columns.
For example: On the node <Predicate>

Like Lamak says, doing this right is nearly impossible, at least in SQL.
To do it properly you should use a SQL Parser for the SQL variant you are using. A SQL Parser will identify the objects referenced in the statement, and the elements of the where clauses.
But if you know that the queries are simple and look like the one you are listing, you can use some simple queries to pick apart the statements. I have made an example for a single statement:
declare
#foo nvarchar(max)=N'Select ID, Code, Phone From Staff Where Phone= ''1234'' And Email = ''a#b'' '
, #From nvarchar(max)=' From '
, #Table nvarchar(max)
declare #a int
-- find FROM
set #a= PATINDEX('%'+#From+'%',#foo)
-- Find statement to the right of from
set #foo=ltrim(RIGHT(#foo,len(#foo)-(#a)))
-- Find first space
set #a=CHARINDEX(' ',#foo)
-- find first word, we assume it is the table name
set #Table=ltrim(rtrim(LEFT(#foo,#a)))
-- Find WHERE statement
set #foo=ltrim(rtrim(replace(right(#foo,len(#foo)-#a),'Where','')))
-- Now find matching columns in table, I am using SQL Server so I look up column names in information_schema.columns
select #Table,column_name
from INFORMATION_SCHEMA.COLUMNS
--from (values ('ID'),('Code'),('Name'),('Phone'),('DOB'),('Email'),('Addr1'),('Addr2'),('Addr3'))cols(column_name)
where TABLE_NAME=#table
and #foo like '%'+column_name+'%'
This solution will only work for some simple statements, it assumes a lot of stuff.

Related

Database query with two fields in the same table. Don't consider if any of the values are empty

The problem is I have a table with 4 columns. I have two search boxes.
Fields
FName
LName
Age
School
Text boxes
FName
School
If the user has inserted two values I want to get the intersect using both values. If only one value is present I want to have data using that value. I thought of not handling this in the application but with a stored procedure.
I thought of using IF ELSE in the stored procedure or having sub queries. But not a solid solution. I need some guidance to think of a possible way. Thank you in advance.
Here is what I have tried. This is just the query I need to embed this in a stored procedure
SELECT * FROM STUDENT WHERE FNname like '%TestFName%'
INTERSECT SELECT * FROM STUDENT WHERE School LIKE '%TestSchool';
If the 'TestSchool" becomes null it takes all the records which full fill the first query.
If both values are missing it returns the whole table.
If both values are there it returns the specific data tuples.
Pretty sure it is as simple as this.
SELECT *
FROM STUDENT
WHERE FNname like '%TestFName%'
AND School LIKE '%TestSchool%';
If use 'AND' rather than 'OR' it meets all the given conditions
SELECT *
FROM YourTable
WHERE (FName = #Fname OR #Fname = '')
AND (School = #School OR #School ='')
Here is the answer I came up for my own problem.
SELECT * FROM STUDENT WHERE FNname like '%TestFName%'
INTERSECT SELECT * FROM STUDENT WHERE School LIKE '%TestSchool';
In here if the 'TestFName" becomes null it takes all the records which full fill the second query.
If the 'TestSchool" becomes null it takes all the records which full fill the first query.
If both values are missing it returns the whole table.
If both values are there it returns the specific data tuples.
Thank you. If there is a better way than this, please enlighten us.
You can use this :
SELECT * FROM STUDENT WHERE isnull(FNname, '') like '%' + isnull(#FNname, '') + '%'
and isnull(School, '') like '%' + isnull(#School, '') + '%'
Use the parameters #FNname and #School in your stored procedure and use the above query in it.

T-SQL Skip cte Table and Union when Variable Is Null

I've got a query that captures all shipments and costs from our factory. Sample data and desired output on Google Drive here:
https://drive.google.com/open?id=0B4xdnV0LFZI1VndEaGgxNDVpU2M
The issue is we've got 2 different ways of selling things. One is 'Regluar' where we make it and the other one is a 'buy/sell' where we buy and sell it.
To capture the costs I've had to write two queries, one for each scenario. The end users of this query can enter in a date range and the query works well then, but I'm stuck when it comes to the variable #Job_No.
All work that goes through our factory (cteRegularJobs) has a Job Number associated with it and I've declared a variable so users can use it to search. The cteBuyandSell has a value of 'NULL' for Job_No declared in the SELECT statement so I can do a UNION of these two tables at the end. However, no buy/sell jobs have Job Numbers assigned to them, they are always NULL.
Initially #Job_No is declared as '' and when it's left '' I want the results from both cte tables returned. If there is an entry by the user, i.e. '001' then I want results for cteRegularJobs.
If it makes it easier I am open to declaring the cteBuyandSell.Job_No something else besides NULL, like "Buy and Sell".
The real query is complicated so here's a simplified example of the structure:
DELCARE #Job_No AS varchar(10) = '';
SET #Job_No = {User Input or leave as ''};
WITH
cteBuyandSell AS ( NULL AS 'Job_No',
...),
cteRegularJobs AS (tblJobs.Job_No AS 'Job_No',
...
WHERE tblJobs.Job_No LIKE #Job_No)
SELECT *
FROM
(cteBuyandSell
UNION
cteRegularJobs)
You can logically break this up with an IF statement to check the value of your variable. I'd suggest NULL over white space though. Here's an example procedure... with the limited code you provided.
CREATE PROCEDURE getData(#Job_No varchar(10) = NULL)
AS
IF #Job_No IS NULL
BEGIN
WITH
cteBuyandSell AS ( NULL AS 'Job_No',
...),
cteRegularJobs AS (tblJobs.Job_No AS 'Job_No',
...
WHERE tblJobs.Job_No LIKE #Job_No)
SELECT *
FROM
(cteBuyandSell
UNION
cteRegularJobs)
END
ELSE
BEGIN
WITH
cteRegularJobs AS (tblJobs.Job_No AS 'Job_No',
...
WHERE tblJobs.Job_No LIKE #Job_No)
SELECT * FROM cteRetularJobs
END

Check if a list of items already exists in a SQL database

I want to create a group of users only if the same group does not exist already in the database.
I have a GroupUser table with three columns: a primary key, a GroupId, and a UserId. A group of users is described as several lines in this table sharing a same GroupId.
Given a list of UserId, I would like to find a matching GroupId, if it exists.
What is the most efficient way to do that in SQL?
Let say your UserId list is stored in a table called 'MyUserIDList', the following query will efficiently return the list of GroupId containing exactly your user list. (SQL Server Syntax)
Select GroupId
From (
Select GroupId
, count(*) as GroupMemberCount
, Sum(case when MyUserIDList.UserID is null then 0 else 1 End) as GroupMemberCountInMyList
from GroupUser
left outer join MyUserIDList on GroupUser.UserID=MyUserIDList.UserID
group by GroupId
) As MySubQuery
Where GroupMemberCount=GroupMemberCountInMyList
There are couple of ways of doing this. This answer is for sql server only (as you have not mentioned it in your tags)
Pass the list of userids in comma seperated to a stored procedure and in the SP create a dynamic query with this and use the EXEC command to execute the query. This link will guide you in this regard
Use a table-valued parameter in a SP. This is applicable to sql server 2008 and higher only.
The following link will help you get started.
http://www.codeproject.com/Articles/113458/TSQL-Passing-array-list-set-to-stored-procedure-MS
Hope this helps.
One other solution is that you convert the input list into a table. This can be done with various approaches. Unions, temporary tables and others. A neat solution combines the answer of
user1461607 for another question here on SO, using a comma-separated string.
WITH split(word, csv) AS (
-- 'initial query' (see SQLite docs linked above)
SELECT
'', -- place holder for each word we are looking for
'Auto,A,1234444,' -- items you are looking for
-- make sure the list ends with a comma !!
UNION ALL SELECT
substr(csv, 0, instr(csv, ',')), -- each word contains text up to next ','
substr(csv, instr(csv, ',') + 1) -- next recursion parses csv after this ','
FROM split -- recurse
WHERE csv != '' -- break recursion once no more csv words exist
) SELECT word, exisiting_data
FROM split s
-- now join the key you want to check for existence!
-- for demonstration purpose, I use an outer join
LEFT OUTER JOIN (select 'A' as exisiting_data) as t on t.exisiting_data = s.word
WHERE s.word != '' -- make sure we clamp the empty strings from the split function
;
Results in:
Auto,null
A,A
1234444,null

SQL Server query brings unmatched data with BETWEEN filter

I'm querying on my products table for all products with code between a range of codes, and the result brings a row that should't be there.
This is my SQL query:
select prdcod
from products
where prdcod between 'F-DH1' and 'F-FMS'
order by prdcod
and the results of this query are:
F-DH1
F-DH2
F-DH3
FET-RAZ <-- What is this value doing here!?
F-FMC
F-FML
F-FMS
How can this odd value make it's way into the query results?
PS: I get the same results if I use <= and >= instead of between.
According to OP request promoted next comment to answer:
Seems like your collation excludes '-' sign - this way results make sense, FE is between FD and FM.
:)
between and >= and <= are primarily used for numeric operations (including dates). You're trying to use this for strings, which are difficult at best to determine how those operators will interpret the each string.
Now, while I think I understand your goal here, I'm not entirely sure it's possible using SQL Server queries. This may be some business logic (thanks to the product codes) that needs implemented in code. Something like the Entity Framework or Linq-to-SQL may be better suited to get you the data you're looking for.
How about adding AND LEFT(prdcod, 2) = 'F-'?
Try replacing the "-" with a space so the order is what you would expect:
DECLARE #list table(word varchar(50))
--create list
INSERT INTO #list
SELECT 'F-DH1'
UNION ALL
SELECT 'F-DH2'
UNION ALL
SELECT 'F-DH3'
UNION ALL
SELECT 'FET-RAZ'
UNION ALL
SELECT 'F-FMC'
UNION ALL
SELECT 'F-FML'
UNION ALL
SELECT 'F-FMS'
--original order
SELECT * FROM #list order by word
--show how order changes
SELECT *,replace(word,'-',' ') FROM #list order by replace(word,'-',' ')
--show between condition
SELECT * FROM #list where replace(word,'-',' ') between 'F DH1' and 'F FMS'

SP to find keywords like a list or strings

In my mssql database I have a table containing articles(id, name, content) a table containing keywords(id, name) and a link table between articles and keywords ArticleKeywords(articleId, keywordID, count). Count is the number of occurrences of that keyword in the article.
How can I write a SP that gets a list of comma separated strings and gives me the articles that have this keywords ordered by the number of occurrences of the keywords in the article?
If an article contains more keywords I want to sum the occurrences of each keyword.
Thanks, Radu
Although it isn't completely clear to me what the source of your comma-separated string is, I think what you want is an SP that takes a string as input and produces the desired result:
CREATE PROC KeywordArticleSearch(#KeywordString NVARCHAR(MAX)) AS BEGIN...
The first step is to verticalize the comma-separated string into a table with the values in rows. This is a problem that has been extensively treated in this question and another question, so just look there and choose one of the options. Whichever way you choose, store the results in a table variable or temp table.
DECLARE #KeywordTable TABLE (Keyword NVARCHAR(128))
-- or alternatively...
CREATE TABLE #KeywordTable (Keyword NVARCHAR(128))
For lookup speed, it is even better to store the KeywordID instead so your query only has to find matching ID's:
DECLARE #KeywordIDTable TABLE (KeywordID INT)
INSERT INTO #KeywordTable
SELECT K.KeywordID FROM SplitFunctionResult S
-- INNER JOIN: keywords that are nonexistent are omitted
INNER JOIN Keywords K ON S.Keyword = K.Keyword
Next, you can go about writing your query. This would be something like:
SELECT articleId, SUM(count)
FROM ArticleKeywords AK
WHERE K.KeywordID IN (SELECT KeywordID FROM #KeywordIDTable)
GROUP BY articleID
Or instead of the WHERE you could use an INNER JOIN. I don't think the query plan would be much different.
For the sake or argument lets say you want to look-up all articles containg the keywords Foo, Bar and Shazam.
ALTER PROCEDURE spArticlesFromKeywordList
#KeyWords varchar(1000) = 'Foo,Bar,Shazam'
AS
SET NOCOUNT ON
DECLARE #KeyWordInClause varchar(1000)
SET #KeyWordInClause = REPLACE (#KeyWords ,',',''',''')
EXEC(
'
SELECT
t1.Name as ArticleName,
t2.Name as KeyWordName,
t3.Count as [COUNT]
FROM ArticleKeywords t3
INNER JOIN Articles t1 on t3.ArticleId = t1.Id
INNER JOIN Keywords t2 on t3.KeywordId = t2.Id
WHERE t2.KeyWord in ( ''' + #KeyWordInClause + ''')
ORDER BY
3 descending, 1
'
)
SET NOCOUNT OFF
I think I understand what you are after so here goes ,(not sure what lang you are using but) in PHP (from your description) I would query ArticleKeywords using a ORDER BY count DESC statement (i.e. the highest comes first) - Obviously you can "select by keywordID or articleid. In very simple terms (cos that's me - simple & there may be much better people than me) you can return the array but create a string from it a bit like this:
$arraytostring .= $row->keywordID.',';
If you left join the tables you could create something like this:
$arraytostring .= $row->keywordID.'-'.$row->name.' '.$row->content.',';
Or you could catch the array as
$array[] = $row->keywordID;
and create your string outside the loop.
Note: you have 2 fields called "name" one in articles and one in keywords it would be easier to rename one of them to avoid any conflicts (that is assuming they are not the same content) i.e. articles name = title and keywords name= keyword