• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

Knitter

Boas práticas no uso de strings SQL longas?

10 mensagens neste tópico

Boas,

Gostaria de conhecer e/ou discutir as técnicas que usam ou conhecem para escrever strings longas dentro de ficheiros de código PHP.

Actualmente vejo-me a escrever código SQL extremamente longo, que quando dividido por +/- 80 caracteres, resulta em muitas linhas de código. Como é natural, quebrar essas linhas tem alguns inconvenientes para a indentação e algumas vezes resultam em alterações do código já que os espaços são esquecidos e a mudança de linha em PHP não se traduz em mudanças de linha na string com SQL.

Sei que posso colocar um \n no início de cada nova linha, mas isso torna a leitura complicada e é incómodo colocar esses caracteres que depois atrapalham quando queremos testar. Deixar um espaço no fim de cada linha anterior dá origem a vários erros, resumindo, como dão a volta a este problema?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Exemplo mais ou menos real duma aplicação que eu fiz.

$sql = <<<SQL_COMMAND
select (c.CR1 * 1000 + c.CR2 * 100) + c.CR3 * 10 + c.CR4 as keyid
     , c.DescriptionCR4 as CName
     , sum(o.VDez) AS MOrc
     , sum(o.VJan + o.VFev + o.VMar + o.VAbr + o.VMai + o.VJun + o.VJul + o.VAgo + o.VSet + o.VOut + o.VNov + o.VDez) AS AOrc
     , sum(o.VJan + o.VFev + o.VMar + o.VAbr + o.VMai + o.VJun + o.VJul + o.VAgo + o.VSet + o.VOut + o.VNov + o.VDez) AS TOrc
from acessos as a
     inner join orcamento as o
           on a.CR1 = o.CR1 and a.CR2 = o.CR2 and a.CR3 = o.CR3 and a.CR4 = o.CR4
     inner join cresp4 as c
           on o.CR1 = c.CR1 and o.CR2 = c.CR2 and o.CR3 = c.CR3 and o.CR4 = c.CR4
where (c.CR1 = {$_SESSION['cr1']})
  and (c.CR2 = {$_SESSION['cr2']})
  and (c.CR3 = {$_SESSION['cr3']})
  and (a.userNumber = {$_SESSION['userid']})
  and (a.useinCustos = 1)
  and (o.ano = {$_SESSION['year']})
group by (c.CR1 * 1000 + c.CR2 * 100) + c.CR3 * 10 + c.CR4
       , c.DescriptionCR4
order by (c.CR1 * 1000 + c.CR2 * 100) + c.CR3 * 10 + c.CR4
SQL_COMMAND;

$res = mssql_query($sql);
/* ... */

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Eu faço mais ou menos como o pmg, a única diferença deve ser como concateno as strings e ponho os comandos sql em maiúsculas para melhor leitura.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Exemplo mais ou menos real duma aplicação que eu fiz.

Ena, eu já fazia isso para os casos onde tinha de escrever HTML, mas nem sei de onde passei a usar isso. Esse tipo de construção é comum no PHP? Como se chama? Para que possa pesquisar mais sobre isso. Estou a falar do que usaste e do que eu uso no caso do HTML:

$var = <<<SQL_COMMAND
...

$var = <<<HTML
...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Eu tambem estou feliz apenas com boa indentação. Se fores uma daquelas pessoas malucas por código todo xpto podes optar por um query builder.

Por exemplo, os gajos do fluxbb usam isto:

function query_build($query, $unbuffered = false)
{
	$sql = '';

	if (isset($query['SELECT']))
	{
		$sql = 'SELECT '.$query['SELECT'].' FROM '.(isset($query['PARAMS']['NO_PREFIX']) ? '' : $this->prefix).$query['FROM'];

		if (isset($query['JOINS']))
		{
			foreach ($query['JOINS'] as $cur_join)
				$sql .= ' '.key($cur_join).' '.(isset($query['PARAMS']['NO_PREFIX']) ? '' : $this->prefix).current($cur_join).' ON '.$cur_join['ON'];
		}

		if (!empty($query['WHERE']))
			$sql .= ' WHERE '.$query['WHERE'];
		if (!empty($query['GROUP BY']))
			$sql .= ' GROUP BY '.$query['GROUP BY'];
		if (!empty($query['HAVING']))
			$sql .= ' HAVING '.$query['HAVING'];
		if (!empty($query['ORDER BY']))
			$sql .= ' ORDER BY '.$query['ORDER BY'];
		if (!empty($query['LIMIT']))
			$sql .= ' LIMIT '.$query['LIMIT'];
	}
	else if (isset($query['INSERT']))
	{
		$sql = 'INSERT INTO '.(isset($query['PARAMS']['NO_PREFIX']) ? '' : $this->prefix).$query['INTO'];

		if (!empty($query['INSERT']))
			$sql .= ' ('.$query['INSERT'].')';

		$sql .= ' VALUES('.$query['VALUES'].')';
	}
	else if (isset($query['UPDATE']))
	{
		$query['UPDATE'] = (isset($query['PARAMS']['NO_PREFIX']) ? '' : $this->prefix).$query['UPDATE'];

		$sql = 'UPDATE '.$query['UPDATE'].' SET '.$query['SET'];

		if (!empty($query['WHERE']))
			$sql .= ' WHERE '.$query['WHERE'];
	}
	else if (isset($query['DELETE']))
	{
		$sql = 'DELETE FROM '.(isset($query['PARAMS']['NO_PREFIX']) ? '' : $this->prefix).$query['DELETE'];

		if (!empty($query['WHERE']))
			$sql .= ' WHERE '.$query['WHERE'];
	}
	else if (isset($query['REPLACE']))
	{
		$sql = 'REPLACE INTO '.(isset($query['PARAMS']['NO_PREFIX']) ? '' : $this->prefix).$query['INTO'];

		if (!empty($query['REPLACE']))
			$sql .= ' ('.$query['REPLACE'].')';

		$sql .= ' VALUES('.$query['VALUES'].')';
	}

	return $this->query($sql, $unbuffered);
}

e depois desatam a escrever queries assim:

   $query = array(
        'SELECT'    => 'p.topic_id, p.posted',
        'FROM'        => 'posts AS p',
        'WHERE'        => 'p.id='.$pid
    );

Como isto não poupa nenhuma digitação nem torna os queries mais visiveis na minha opinião, tem pouco valor para mim.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Isso é estilo Active Record. Dá jeito para certas situações, mas para coisas mais complexas é difícil fugir à maneira tradicional.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Eu vou colocar um exemplo do que estou a falar:

        $query =  "SELECT
                    tm.cod_cat AS ufcd,
                    cl.cod AS action,
                    u.idc_number AS docNumber,
                    #__training_aux_idc.dsc AS docType,
                    UCASE( u.name ) AS uName,
                    Date_Format( u.birthdate, '%Y-%m-%d' ) AS birthdate,
                    IF( u.sex = 'M', 'M-Masculino', 'F-Feminino' ) AS sex,
                    UCASE(u.address1) AS address,
                    u.address3 AS postal,
                    u.phone AS phoneNumber,
                    #__training_aux_habilitation.dsc AS school,
                    ajs.dsc AS jobDsc,
                    IF( ce.cno_source = 0, 'N - Não', 'S - Sim' ) AS cnoSource,
                    IF( ce.cno_source = 0, '', ce.cno_description ) AS cnoDescription,
                    #__training_aux_company_nb.dsc AS companySize,
                    cl.year AS clYear,
                    tc.duration AS hRoom,
                    IF( cl.regime = 1 AND ajs.dsc LIKE 'Empregado', tc.duration, '') As hRoomWorkPeriod,
                    IF( cl.regime = 0 AND ajs.dsc LIKE 'Empregado', tc.duration, '') As hRoomAfterWorkPeriod,
                    ets.description AS traineeStatus,
                    IF( cl.regime = 1 AND ajs.dsc LIKE 'Empregado', (SELECT count(s.`date`) FROM sl_training_class_session s WHERE class = $class), '') AS daysL,
                    IF( cl.regime = 0 AND ajs.dsc LIKE 'Empregado', (SELECT count(s.`date`) FROM sl_training_class_session s WHERE class = $class), '') AS daysP,
                    (SELECT count(s.`date`) FROM sl_training_class_session s WHERE class = $class) AS daysTotal
                FROM #__training_class_enroll ce RIGHT JOIN #__training_class cl
                    ON cl.id = ce.class LEFT JOIN #__training_course tc
                    ON cl.course = tc.id LEFT JOIN #__users u
                    ON ce.enrolle = u.id LEFT JOIN #__training_module tm
                    ON tc.id = tm.course LEFT JOIN #__training_aux_idc
                    ON u.idc_type = #__training_aux_idc.id LEFT JOIN #__training_aux_habilitation
                    ON u.habilitation = #__training_aux_habilitation.id LEFT JOIN #__training_aux_job_status ajs 
                    ON u.job_status = ajs.id LEFT JOIN #__training_needs
                    ON u.id = #__training_needs.id_user LEFT JOIN #__training_aux_company_nb
                    ON #__training_needs.entity_job =  #__training_aux_company_nb.id LEFT JOIN #__training_aux_enrolled_trainee_status ets
                    ON ets.id = ce.enrolled_status
                WHERE cl.id = $class AND ce.status = 1
                GROUP BY u.name
                ORDER BY u.name;

Este nem é dos maiores... e como podem ver dá lugar à ocorrência de vários erros porque se podem esquecer de colocar um espaço e as quebras da string não são quebras de linha. Por outro lado não é fácil de indentar.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

É de facto longo. Mas se queres a minha opinião parece-me arrumadinho. Não estou a ver como ter essa funcionalidade toda com mais arrumação.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

O problema não é o "arrumadinho", porque indentação podemos fazer sempre, o problema, se reparares, é que todas as linhas têm, de ter um espaço no fim para que não fiquem coladas às de baixo. No caso da linha terminar com vírgulas não há stress mas se não terminar tens linhas coladas e erros de SQL eu são chatos de identificar. Além disso, o meu objectivo é criar um sistema comum para todos os programadores da equipa para que não hajam confusões.

Esse exemplo foi indentado por mim, mas já outras linhas de SQL tão longas em que o programador fez de cada linha uma string e usa o operador de concatenação, ".", para juntar as linhas, aí ainda dá origem a mais erros. Outros resolvem esse problema com o uso de "\n" no início, resumindo, cada um faz à sua maneira e não é nada agradável :D

Como neste momento estou responsável por, além de programar, fazer o estudo e implementação de sistemas de backups, metodologias de desenvolvimento para a empresa, metodologias de organização da empresa, de ferramentas a usar e de configurar alguns servidores para suportarem todos os novos processos, resolvi aproveitar e arrumar logo com todos os pontos que possam causar confusão. E as diferenças entre estilos de programação já deram lugar a muitos bugs.

Pelo que estive a ver, acho que optar por usar heredoc (obrigado pmg pelo nome :P) deve resolver o problema, as strings comportam-se como se delimitadas por aspas, expandem as variáveis como é necessário e identifica bem, no meio do PHP, o código a ter especial atenção, neste caso SQL ou HTML nos pontos onde é necessário.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora