0

I have been tasked with writing a function which will be passed a sql query as a string, it should parse this query prepending "tablename" to any tables it finds. I have been given some code as an example (its simply looking for instances of "from" and "where/inner/outer/left/right" etc.. and assuming that the word in the middle is a table... This is being done in C#

All Im really looking for is pointers of the best way to do this... (Although the brief is I will be passed a string and I will return a string, so please bear that in mind if you have any ideas!)

I have tried using Sql parser for the below sample queries but it does not help me out

  //"SELECT e.last_name,       e.department_id,       d.department_name FROM   employees e       LEFT OUTER JOIN department d         ON ( e.department_id = d.department_id ); "
   //"SELECT   a.Id, a.Name  AS Surname, b.preName, c.Busrel_Id FROM  Customer a  LEFT JOIN CustomerDetail b ON a.Id = b.fId   LEFT JOIN Businessrel c            ON b.cId = c.Bid         JOIN Contract d           ON c.Id = d.fId  AND a.DATE < '20071204:13.23.14.000' AND a.cst_Class_cd IN ('01','02') AND b.Name LIKE 'W%'  AND (SELECT MAX(e.TIMESTAMP) FROM   dDate e  WHERE  e.colLid = a.Id) ORDER BY a.Name, b.preName";
     //SELECT   Quantity,b.Time,c.Description FROM   (SELECT ID,Time FROM bTab) b  INNER JOIN aTab a on a.ID=b.ID  INNER JOIN cTab c on a.ID=c.ID

code which i used was

using gudusoft.gsqlparser;
using gudusoft.gsqlparser.Units;

TSelectSqlStatement stmt = (TSelectSqlStatement)sqlparser.SqlStatements[0];
                foreach (TLzField lcfield in stmt.Fields)
                {
                    lctablename = "NULL";
                    lcfieldalias = "NULL";   

                    if (lcfield.FieldPrefix.Length > 0)
                    {
                        lctablename = findtablename(stmt.Tables, lcfield.FieldPrefix);
                    }

                    lcfieldname = lcfield.FieldName;

                    if (lcfield.FieldAlias.Length > 0)
                    {
                        lcfieldalias = lcfield.FieldAlias;
                    }

                    Console.WriteLine("{0},{1},{2}", lctablename, lcfieldname, lcfieldalias);
                }

public static string findtablename(TLzTableList tbl, string tname)
        {
            foreach (TLzTable tb in tbl)
            {
                if (tb.TableName.CompareTo(tname) == 0)
                {
                    return tb.TableName;
                }
                else if (tb.TableAlias.CompareTo(tname) == 0)
                {
                    return tb.TableName;
                }
            }
            return tname;
        }

What ever the query listed out such as

1)nested query 2)sub queries 3)joint queries

i am unable to retrieve table name specified in that query

waiting for your responses and comments

2 Answers 2

3

A SQL parser does seem like the better solution, but I thought I'd try to write a regex anyway. This regular expression detects all the table names in the sample strings you provided. It's a bit messy and it's certainly possible that there are cases that this regex does not cover. It must be used with the IgnoreCase option.

(?<=(?:FROM|JOIN)[\s(]+)(?>\w+)(?=[\s)]*(?:\s+(?:AS\s+)?\w+)?(?:$|\s+(?:WHERE|ON|(?:LEFT|RIGHT)?\s+(?:(?:OUTER|INNER)\s+)?JOIN)))

Here is a piece of sample code that prefixes all the table names with "dbo.".

const string REGEX_MATCH_TABLE_NAME = @"(?<=(?:FROM|JOIN)[\s(]+)(?>\w+)(?=[\s)]*(?:\s+(?:AS\s+)?\w+)?(?:$|\s+(?:WHERE|ON|(?:LEFT|RIGHT)?\s+(?:(?:OUTER|INNER)\s+)?JOIN)))";
string testInput = "SELECT e.last_name,e.department_id, d.department_name FROM   employees e LEFT OUTER JOIN department d ON ( e.department_id = d.department_id );";
string resultString = Regex.Replace(testInput, REGEX_MATCH_TABLE_NAME, "dbo.$0", RegexOptions.IgnoreCase | RegexOptions.Multiline);

Here is a rough breakdown of the regex:

(?<=                #Begin positive lookbehind.
    (?:FROM|JOIN)   #Match keywords that usually preceed a table name.
    [\s(]+
)                   #End positive lookbehind.
(?>\w+)             #Match the table name. Atomic for faster failed matches.
(?=                 #Begin positive lookahead.
    [\s)]*
    (?:\s+(?:AS\s+)?\w+)?                                               #Match the table alias that possibly follows the table name.
    (?:$|\s+(?:WHERE|ON|(?:LEFT|RIGHT)?\s+(?:(?:OUTER|INNER)\s+)?JOIN)) #Match keywords that usually follow a table name.
)                   #End positive lookahead.
Sign up to request clarification or add additional context in comments.

2 Comments

Hi, if I use an query without joins but put table after from ( select * from tableA, tableB where tableA.ID = tableB.ID) the Regex don't work. How can I do in that case? Thanks.
I ran the code and realize the resultstring is exactly the same as input.....
0

If I didn't understand your question correctly, please reply me.

Run the below query against your DB,

 SELECT * FROM information_schema.tables

Retrieve the table name and append it.

1 Comment

Let us consider the situation that u do not have connection related properties only ur having a command with you for ex: select * from table1 what i need to take only the table from the above command

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.