3

I am creating a function to easily retrieve a client name from a user account record based on the available information. All of the account fields other than id are optional and may be blank, so this successively checks various identifying columns until one is found, returning the id as a last result.

CREATE FUNCTION ACCOUNT_NAME(
    fname varchar(50),
    lname varchar(50),
    company varchar(50),
    email varchar(50),
    id int(10) unsigned
)
RETURNS VARCHAR(100) DETERMINISTIC CONTAINS SQL SQL SECURITY INVOKER
RETURN
    IF( TRIM(CONCAT(fname, ' ', lname)) <> '',
        TRIM(CONCAT(fname, ' ', lname)),
        IF( company <> '',
            company,
            IF(email <> '', email, id)
        )
    )
;

This works fine, however I need to pass each column into the function individually, i.e.:

SELECT ACCOUNT_NAME(
    a.first_name,
    a.last_name,
    a.company,
    a.email,
    a.id
) AS client_name
FROM accounts AS a WHERE 1;

Not only is this tedious, it will be extremely difficult to modify or extend this function in the future, likely requiring finding and updating every invocation as well as the definition itself.

Is it possible to pass a reference to the entire row result as an input parameter? Ideally I would like to do something like this:

CREATE FUNCTION ACCOUNT_NAME(row result)
    RETURNS VARCHAR(100) DETERMINISTIC CONTAINS SQL SQL SECURITY INVOKER
RETURN
    IF( TRIM(CONCAT(row.first_name, ' ', row.last_name)) <> '',
        TRIM(CONCAT(row.first_name, ' ', row.last_name)),
        IF( row.company <> '',
            row.company,
            IF(row.email <> '', row.email, row.id)
        )
    )
;

SELECT ACCOUNT_NAME(a.*) AS client_name
FROM accounts AS a WHERE 1;
6
  • But you can just pass the id, and then select the row with that ID inside the function, and then you have the entire row... Commented Dec 26, 2013 at 20:31
  • Interesting, I'll give it a shot, but wouldn't that have an enormous performance impact to query each of potentially hundreds of rows individually when I already have all of the necessary data already? Commented Dec 26, 2013 at 20:37
  • I do not know the details for your query, or how much data you have, have you looked into a cursor? Why do you use a function? Maybe a stored procedure/cursor combination would do all of this really fast in just one shot. Commented Dec 26, 2013 at 20:39
  • @Rob - Make it work first and worry about performance afterwards, if it is an issue. Commented Dec 26, 2013 at 21:17
  • Why are you writing something which duplicates the MySQL COALESCE() built-in function? Commented Dec 27, 2013 at 6:25

2 Answers 2

2
+50

The answer to your literal question is no—the parameters of a stored procedure or stored function cannot be a reference to a row object. They must be scalar data types, like the data types you use for defining columns in a table.

You could define a VIEW instead:

CREATE OR REPLACE VIEW accounts_view (id, client_name) AS
  SELECT id, COALESCE(
    NULLIF(CONCAT(fname, ' ', lname), ' '),
    NULLIF(company, ''),
    NULLIF(email, ''),
    id) AS client_name
  FROM accounts;

Then you can query it more simply:

SELECT client_name
FROM accounts_view;

If you ever change the way you want client_name to be formatted, then just use another CREATE OR REPLACE VIEW statement. It is very quick to re-write a view definition.

Sign up to request clarification or add additional context in comments.

1 Comment

This looks like the closest thing to having a row as a parameter.
0

SQL is not a good language for text manipulation. SQL is a good language for storing and retrieving data.

Keep the "business logic" in the application code. That is, have a Layer of code (in whatever language you are using) that performs the Account_Name manipulation.

The "client" can call the Layer with a row reference or whatever makes sense. Design the API to keep it clean. The Layer then does whatever grungy work is needed to implement functions like this. It may even have to run off to more than one table to gather the pieces.

For example, today you have the email in the same table. If tomorrow you have it somewhere else, then change the Layer to do two SELECTs or (better) one SELECT with a JOIN.

Comments

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.