Question

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

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top