Como instalar um servidor LAMP no openSUSE

Um servidor Linux, com serviços Apache, banco de dados MySQL ou MariaDB e a linguagem de scripts PHP é um ambiente muito comum para desenvolvedores web, administradores de servidores web etc.
Há variações para esta configurações.
Alguns administradores ou desenvolvedores preferem usar outros servidores web, que não o Apache. Também podem optar por outro banco de dados que não o MySQL — neste caso, o PostreSQL e o MariaDB são bastante comuns.

A tendência atual de distros como openSUSE, Ubuntu, Fedora, entre outras, é a substituição do MySQL pelo MariaDB

Como opção ao PHP, surgem o Python e o Perl, entre outros.
Neste texto, vamos nos ater ao LAMP tradicional, em uma maquina openSUSE Leap 42.1 — até por que ele é muito fácil de instalar.
Se quiser saber mais sobre como verificar a versão do openSUSE em uso no seu sistema, leia este texto.
Se, posteriormente, quiser acrescentar suporte ao Perl, Python ou Ruby on Rails, isto pode ser feito de modo muito natural.

Como encontrar o padrão de instalação LAMP no openSUSE

A ferramenta zypper, de gestão de pacotes, permite encontrar rapidamente qualquer coisa que esteja dentro dos repositórios do sistema operacional.
Se quiser saber mais sobre o zypper, leia este texto.
No exemplo que segue, a gente começa com um refresh, para sincronizar o nosso cache local com os repositórios remotos. Em seguida faremos uma busca, com o comando search, pelos padrões (patterns) que contenham a string “lamp”. Veja abaixo:

sudo zypper update
zypper search --type pattern lamp
Carregando dados de repositório...
Lendo os pacotes instalados...

S | Nome        | Resumo                   | Tipo  
--+-------------+--------------------------+-------
  | lamp_server | Servidor Internet e LAMP | padrão

Quer obter mais informações sobre o pattern lamp_server?
Use o parâmetro info:

zypper info -t pattern lamp_server
.
Carregando dados de repositório...
Lendo os pacotes instalados...


Informação para padrão lamp_server:
-----------------------------------
Repositório: Repositório principal de atualização
Nome: lamp_server
Versão: 20150918-12.1
Arquitetura: x86_64
Fornecedor:openSUSE
Instaladas: Não
Visível ao Usuário: Sim
Resumo:Servidor Internet e LAMP
Descrição: 
  Programa para configurar um servidor de Internet que possa lidar com conteúdo
  estático, dinâmico e interativo (como uma loja da Internet). Isso inclui o
  servidor HTTP Apache, o sistema de gerenciamento de banco de dados MySQL e
  linguagens de script como PHP, Python, Ruby on Rails ou Perl.
Conteúdo:

S | Nome                          | Tipo    | Dependência
--+-------------------------------+---------+------------
  | apache2                       | package |            
  | apache2-doc                   | package |            
  | apache2-example-pages         | package |            
  | apache2-mod_perl              | package |            
  | apache2-mod_php5              | package |            
  | apache2-mod_python            | package |            
  | apache2-prefork               | package |            
  | mariadb                       | package |            
i | patterns-openSUSE-base        | package |            
  | patterns-openSUSE-lamp_server | package |            
  | php5-ctype                    | package |            
  | php5-dom                      | package |            
  | php5-iconv                    | package |            
  | php5-mysql                    | package |            
  | yast2-http-server             | package |  

Observe que na relação de pacotes a ser instalada, o banco de dados MariaDB é default nesta versão do openSUSE.
Para, enfim, fazer a instalação deste pattern, use o comando install:

sudo zypper install -t pattern lamp_server
root's password:
Carregando dados de repositório...
Lendo os pacotes instalados...
Resolvendo dependências de pacote...

The following 32 NEW packages are going to be installed:
  apache2 apache2-doc apache2-example-pages apache2-mod_dnssd apache2-mod_perl
  apache2-mod_php5 apache2-mod_python apache2-prefork apache2-utils libapr1
  libapr-util1 libmysqlclient18 libmysqlclient_r18 libmysqlcppconn7
  libreoffice-base-drivers-mysql mariadb mariadb-client mariadb-errormessages
  patterns-openSUSE-lamp_server perl-Linux-Pid php5 php5-ctype php5-dom
  php5-iconv php5-json php5-mysql php5-pdo php5-sqlite php5-tokenizer
  php5-xmlreader php5-xmlwriter yast2-http-server

The following NEW pattern is going to be installed:
  lamp_server

The following 18 recommended packages were automatically selected:
  apache2 apache2-doc apache2-example-pages apache2-mod_perl apache2-mod_php5
  apache2-mod_python apache2-prefork mariadb php5-ctype php5-dom php5-iconv
  php5-json php5-mysql php5-sqlite php5-tokenizer php5-xmlreader php5-xmlwriter
  yast2-http-server

32 novos pacotes para instalado.
Tamanho total do download: 22,5 MiB. Já em cache: 0 B. Após a operação, 161,3
MiB adicionais serão utilizados.
deseja continuar? [s/n/? mostrar todas as opções] (s): 

Não se esqueça de ler atenciosamente o texto que segue o processo de instalação e confirmar aonde for apropriado.

Pós instalação

Antes de dar o trabalho por encerrado, vamos testar a instalação.
Comece por verificar o estado atual do (MySQL) MariaDB:

sudo rcmysql status
mysql.service - MySQL server
   Loaded: loaded (/usr/lib/systemd/system/mysql.service; disabled)
   Active: inactive (dead)

Ok. As palavras disabled e dead — respectivamente, “desabilitado” e “morto” — não são muito animadoras.
Então vamos colocar o banco de dados no ar!

sudo rcmysql start
sudo rcmysql status

Agora, a mensagem é outra:

mysql.service - MySQL server
   Loaded: loaded (/usr/lib/systemd/system/mysql.service; disabled)
   Active: active (running) since Sat 2016-05-14 19:42:27 BRT; 4s ago
  Process: 4524 ExecStartPost=/usr/lib/mysql/mysql-systemd-helper wait default (code=exited, status=0/SUCCESS)
  Process: 4514 ExecStartPre=/usr/lib/mysql/mysql-systemd-helper upgrade default (code=exited, status=0/SUCCESS)
  Process: 4423 ExecStartPre=/usr/lib/mysql/mysql-systemd-helper install default (code=exited, status=0/SUCCESS)
 Main PID: 4523 (mysqld)
   CGroup: /system.slice/mysql.service
           └─4523 /usr/sbin/mysqld --defaults-file=/etc/my.cnf --user=mysql

May 14 19:42:26 linux-ewci.suse mysql-systemd-helper[4523]: 160514 19:42:26 [Note] InnoDB: Initializing buffer pool, size = 128.0M
May 14 19:42:26 linux-ewci.suse mysql-systemd-helper[4523]: 160514 19:42:26 [Note] InnoDB: Completed initialization of buffer pool
May 14 19:42:26 linux-ewci.suse mysql-systemd-helper[4523]: 160514 19:42:26 [Note] InnoDB: Highest supported file format is Barracuda.
May 14 19:42:26 linux-ewci.suse mysql-systemd-helper[4523]: 160514 19:42:26 [Note] InnoDB: 128 rollback segment(s) are active.
May 14 19:42:26 linux-ewci.suse mysql-systemd-helper[4523]: 160514 19:42:26 [Note] InnoDB: Waiting for purge to start
May 14 19:42:26 linux-ewci.suse mysql-systemd-helper[4523]: 160514 19:42:26 [Note] InnoDB:  Percona XtraDB (http://www.percona.com) 5.6.26-74.0 started; log sequence number 1616707
May 14 19:42:26 linux-ewci.suse mysql-systemd-helper[4523]: 160514 19:42:26 [Note] Server socket created on IP: '::'.
May 14 19:42:26 linux-ewci.suse mysql-systemd-helper[4523]: 160514 19:42:26 [Note] /usr/sbin/mysqld: ready for connections.
May 14 19:42:26 linux-ewci.suse mysql-systemd-helper[4523]: Version: '10.0.22-MariaDB'  socket: '/var/run/mysql/mysql.sock'  port: 3306  openSUSE package
May 14 19:42:27 linux-ewci.suse mysql-systemd-helper[4524]: MySQL is alive

Ajuste a senha do root no MariaDB

Caso você não saiba, o root do banco de dados MySQL/MariaDB é diferente do root do Linux.
Use o seguinte comando, no terminal:

/usr/bin/mysqladmin -u root password 'nova-senha'

Substitua ‘nova-senha’ pela sua senha.

Remova as revisões do banco de dados para melhorar a performance do WordPress.

As revisões são versões ou cópias de segurança de seus posts no WordPress, gravadas a cada vez em que você salva um rascunho (ou draft).
Se você é do tipo que grava “preventivamente” um rascunho a cada parágrafo digitado, provavelmente terá várias revisões salvas para cada artigo criado em seu blog.
WordPress Oficial logo
O recurso é muito útil e pode salvar horas de trabalho, em caso de perda acidental de conexão (entre outras situações possíveis).
No que toca as revisões, o comportamento padrão do WordPress, é:

  • fazer uma gravação automática a cada 60 segundos — o que gera uma cópia do seu texto, como revisão e
  • guardar até 3 revisões.

Por que você deve remover as revisões do banco de dados

Este texto não trata da desativação do recurso de criar revisões, do WordPress.
As revisões continuarão a ser criadas, após a realização do procedimento.
Como já disse, é um recurso de segurança que o WordPress provê a seus usuários e que têm grande valor no momento em que você mais precisar.
Isto posto, é importante que se diga que ter várias versões de seus posts gravadas, nem de longe, equivale a uma política séria de fazer backup do seu site.
No máximo, as revisões ajudam a retomar um trabalho interrompido abruptamente a partir de um certo ponto ou de uma linha de pensamento.
Todas as revisões de artigos escritos há um ano ou mais, para a maioria das pessoas, não têm utilidade alguma, contudo.
Todos os registros que não tem utilidade, estão ocupando espaço à toa no seu banco de dados.
Portanto, com o objetivo de manter o seu banco enxuto — em prol da eficiência e da velocidade do seu site — é que este artigo propõe remover este “excesso de bagagem”.
O processo é simples e seguro.
Pode ser realizado por usuários leigos em MySQL, sem problema algum. Ainda assim, tenho 2 recomendações a fazer, antes de prosseguir:

  • Verifique se seus backups estão em dia. Se estiverem, faça mais uma cópia de segurança de todo o conteúdo do seu blog (só por precaução).
  • Se não se sentir seguro, consulte o administrador do banco de dados do seu provedor ou algum usuário mais experiente, antes de seguir em frente.

Além disto, sugiro que você leia o texto inteiro, antes de aplicar o procedimento.
Se você não sabe como acessar o banco de dados do seu site, este texto não é para você. Desculpe.

Como remover os registros de revisões do meu blog WordPress?

Você precisa acessar o banco de dados do seu site e executar uma query SQL, que vai realizar a tarefa de encontrar os posts marcados como ‘revision’ e apagá-los.
Uma das formas de acessar o banco de dados de um site WordPress é com o uso do PhpMyAdmin, presente em muitas instalações — mas você pode usar a que lhe for mais conveniente.
Captura de tela de 2015-12-05 11:11:29
Uma vez conectado ao seu banco de dados, siga os passos:

  • selecione a tabela que contém seus posts.
    Fique atento ao fato de que ela provavelmente terá um prefixo — portanto adeque o meu exemplo à sua realidade aí.
    O nome da minha tabela, que contém posts, é wp_bch4ind_posts — em destaque, o meu prefixo.
    Se você estiver usando o PhpMyAdmin, selecione a tabela de posts, clicando nela, no painel à esquerda.
  • Em seguida, clique no botão SQL, na barra de ferramentas superior do PhpMyAdmin.
  • Dentro do editor SQL, digite a seguinte declaração:
    DELETE FROM wp_posts WHERE post_type = "revision";
    

    Mais uma vez, não esqueça de substituir o termo wp_posts pelo nome correto da sua tabela.

O que esta query faz é remover todos os posts do seu banco de dados cujo tipo seja igual a ‘revision’.

Resultados que o procedimento oferece

Este procedimento deve ser adotado por quem deseja melhorar o desempenho do carregamento do seu blog para seus leitores.
Para saber quantas revisões você tem no banco de dados, use a seguinte declaração SQL:

SELECT * FROM wp_posts WHERE post_type = "revision";

Se você tiver menos de 1000 posts do tipo ‘revision’, provavelmente não sentirá diferença na velocidade de carregamento do conteúdo do site.
Ainda assim, manter o banco de dados enxuto deve fazer parte da política de manutenção do seu site.
Para obter melhorias significativas na performance do seu blog, várias outras medidas devem ser tomadas em conjunto com esta.
O plugin revisions, pode automatizar esta tarefa para você.
Mas, se você vem perseguindo a melhora de desempenho do seu site WordPress, instalar um novo plugin, talvez não seja a melhor ideia — embora você sempre possa desinstalá-lo, logo após o seu uso —. Você não vai precisar executar este procedimento mais de uma vez por ano, afinal.

Como encontrar itens associados ao maior ou ao menor valor de uma lista no MySQL

Para listar itens e ainda associá-los aos maiores ou menores valores use funções do MySQL em conjunto com as funções MAX() e MIN().
Veja alguns exemplos em que 2 queries são combinadas para obter resultados mais significativos.
As funções MIN() e MAX() são úteis para encontrar os extremos de uma relação de valores. O problema é que, ás vezes, queremos também saber a que item na tabela o valor em questão se encontra associado.
em uma relação de clientes correntistas, podemos usar a função MAX() para determinar qual o maior valor de crédito. O problema a ser resolvido é o de determinar exatamente a quem pertence o tal valor.
Veja um exemplo prático, no qual vamos determinar qual o maior valor de créditos possuído por cliente, na tabela CadastroClientes:

SELECT MAX(CreditosCliente) FROM CadastroClientes;

Como você pode ver, abaixo, o maior valor é 4992:

+----------------------+
| MAX(CreditosCliente) |
+----------------------+
|                 4992 |
+----------------------+
1 row in set (0.01 sec)

E se você quiser saber a que cliente este valor está associado?
Uma das soluções mais óbvias seria a seguinte:

SELECT NomeCliente, MAX(CreditosCliente) FROM CadastroClientes WHERE CreditosCliente = MAX(CreditosClente);

ERROR 1111 (HY000): Invalid use of group function

Infelizmente, ela não funciona.
Por mais que a lógica desta query pareça boa (ela não é), o fato é que funções agregadas, tais como MIN() e MAX(), não podem ser usadas em cláusulas WHERE.
A intenção da consulta é determinar qual registro tem o maior valor de crédito e exibir o nome do cliente associado a este valor.
O problema é que enquanto eu e você entendemos a consulta, pro MySQL ela não faz sentido algum.
Ela não funcionou porque, o MySQL usa a cláusla WHERE para determinar quais registros selecionar, mas só conhece o valor de uma função agregada depois de selecionar os registros a partir dos quais o valor da função é determinado.
A declaração se contradiz, portanto.
A solução, abaixo, é uma gambiarra e, embora chegue à conclusão correta, não responde diretamente ao enunciado:

SELECT NomeCliente, CreditosCliente FROM CadastroClientes ORDER BY CreditosCliente DESC LIMIT 1;
+------------------+-----------------+
| NomeCliente      | CreditosCliente |
+------------------+-----------------+
| Camille Thornton |            4992 |
+------------------+-----------------+
1 row in set (0.00 sec)

Você pode solucionar o problema usando uma abordagem em 2 estágios ou um subselect.
No primeiro estágio, atribuo o maior valor de créditos da tabela à variável CreditoMax:

SELECT @CreditoMax := MAX(CreditosCliente) FROM CadastroClientes;
+-------------------------------------+
| @CreditoMax := MAX(CreditosCliente) |
+-------------------------------------+
|                                4992 |
+-------------------------------------+
1 row in set (0.00 sec)

No segundo estágio, já tenho condições de criar uma consulta que encontre o valor máximo, relacionado ao cliente:

SELECT NomeCliente AS Cliente, @CreditoMax AS "Créditos" FROM CadastroClientes WHERE CreditosCliente = @CreditoMax;
+------------------+-----------+
| Cliente          | Créditos  |
+------------------+-----------+
| Camille Thornton |      4992 |
+------------------+-----------+
1 row in set (0.00 sec)

Nas versões mais atuais do MySQL, é possível resolver o problema com uma subselect, em apenas uma linha de código:

SELECT NomeCliente AS Cliente, CreditosCliente AS "Créditos" FROM CadastroClientes WHERE CreditosCliente = (SELECT MAX(CreditosCliente) FROM CadastroClientes);
+------------------+----------+
| Cliente          | Créditos |
+------------------+----------+
| Camille Thornton |     4992 |
+------------------+----------+
1 row in set (0.00 sec);

Mais um exemplo

No próximo exemplo, vou usar uma tabela que contém a Bíblia.
O objetivo é estabelecer qual o menor versículo presente no livro.
Primeiro, vamos estabelecer quantos caracteres tem o menor versículo da bíblia:

SELECT MIN(LENGTH(textover)) FROM bibliakjv;
+-----------------------+
| MIN(LENGTH(textover)) |
+-----------------------+
|                    13 |
+-----------------------+
1 row in set (0.02 sec)

Que tal uma resposta mais completa — que mostre a quantidade de caracteres do menor versículo da bíblia e o texto do mesmo?
Novamente, vejamos como solucionar o problema em 2 estágios (definindo uma variável e operando com ela, em seguida):

SELECT @MenorVerso := MIN(LENGTH(textover)) FROM bibliakjv;
+--------------------------------------+
| @MenorVerso := MIN(LENGTH(textover)) |
+--------------------------------------+
|                                   13 |
+--------------------------------------+
1 row in set (0.01 sec)

Uma vez atribuído o valor da menor quantidade de caracteres à variável @MenorVerso, vamos usá-lo na próxima consulta:

SELECT numliv AS "Livro", numcap AS "Capítulo", numver AS "Versículo", textover AS "Texto do versículo" FROM bibliakjv WHERE LENGTH(textover) = @MenorVerso\G
*************************** 1. row ***************************
             Livro: 5
          Capítulo: 5
         Versículo: 17
Texto do versículo: Não matarás. 
Núm. de caracteres: 13
1 row in set (0.02 sec)

Note que, na minha versão da Bíblia, há um espaço a mais no versículo, logo após o ponto final — que é contado e, portanto, dá 13 caracteres ao versículo. Paciência… 😉
Se você prefere uma solução usando uma subquery, lá vai:

SELECT numliv AS "Livro", numcap AS "Capítulo", numver AS "Versículo", textover AS "Texto do versículo", @MenorVerso AS "Núm. de caracteres" FROM bibliakjv WHERE LENGTH(textover) = (SELECT MIN(LENGTH(textover)) FROM bibliakjv)\G
*************************** 1. row ***************************
             Livro: 5
          Capítulo: 5
         Versículo: 17
Texto do versículo: Não matarás.
Núm. de caracteres: 13
1 row in set (0.05 sec)

Use DISTINCT para eliminar redundâncias no MySQL

O DISTINCT pode ser usado para determinar quantos valores redundantes há numa lista ou, pelo contrário, quantos valores distintos há presentes.
Use a cláusula DISTINCT para selecionar os valores únicos ou agregue funções com COUNT(DISTINCT) para contá-los.
O DISTINCROW, caso você já tenha ouvido falar, é sinônimo do DISTINCT — use o que te deixa mais confortável, portanto.
Um relatório que não use funções agregadas é usado para determinar quais valores ou quais registros estão contidos no banco de dados, pela eliminação das redundâncias.
O DISTINCT é útil para reduzir o resultado de uma declaração à sua essência. Pode ser combinado com ORDER BY para ordenar a lista.
Por exemplo, se quiser saber os nomes dos clientes listados na tabela de cli_tabela, use a seguinte consulta:

SELECT DISTINCT cli_nome FROM cli_tabela ORDER BY cli_nome;
_
+----------+
| cli_nome |
+----------+
| Ian      |
| Henry    |
| Justin   |
+----------+

Uma declaração sem o uso da cláusula DISTINCT e ORDER BY produz uma relação com os mesmos nomes, só que não é tão fácil de entender.
Veja como ficaria:

SELECT cli_nome FROM cli_tabela;
_

O resultado contém informação demais e fora de ordem:

+----------+
| cli_nome |
+----------+
| Ian      |
| Justin   |
| Henry    |
| Henry    |
| Ian      |
| Henry    |
| Justin   |
| Henry    |
| Ian      |
| Henry    |
+----------+

Mesmo a relação contendo 10 registros de nomes de clientes, temos na verdade apenas 3 nomes de clientes nesta relação.
Se você quiser obter apenas uma contagem dos nomes de clientes registrados na tabela, use COUNT(DISTINCT). Veja um exemplo:

SELECT COUNT(DISTINCT cli_nome) FROM cli_tabela;

como já sabemos, o resultado é…

+--------------------------+
| COUNT(DISTINCT cli_nome) |
+--------------------------+
|                        3 |
+--------------------------+

A cláusula COUNT(DISTINCT) ignora valores NULL.
Se, por acaso, você tiver interesse em incluir na conta estes tipos de valores, experimente o seguinte artifício:

Substitua, na query acima, a variável valor pelo campo desejado.
As consultas com DISTINCT, comumente são úteis quando combinadas com funções agregadas — podendo produzir uma caracterização mais completa de seus resultados.
Por exemplo, o uso da função COUNT(*) em uma tabela de clientes indica a quantidade de clientes presente.
Ao aplicar DISTINCT aos campos ‘estado’, por exemplo, revela-se em quais estados você tem clientes.
Se usar a combinação COUNT(DISTINCT), então, é possível saber por quantos estados se estende sua clientela.
Ao ser usado com múltiplas colunas, o DISTINCT mostra as diferentes combinações de valores e COUNT(DISTINCT) conta o número de combinações.

Como somar valores no MySQL

Em uma coluna ou em um conjunto de números, pode ser necessário determinar a soma. Use a função SUM() do MySQL para realizar esta operação.
Para começar o texto com um exemplo, veja um envolvendo todos os valores da coluna ‘CreditCliente’, que contem uma relação de créditos dos clientes na tabela ‘clientesEstaduais‘.

SELECT SUM(CreditCliente) FROM clientesEstaduais;
+--------------------+
| SUM(CreditCliente) |
+--------------------+
|             946227 |
+--------------------+

Veja a relação de clientes do Ceará (CE):

SELECT * FROM clientesEstaduais WHERE RegiaoCliente = 'CE';
+----+---------------+---------------+--------------+---------------+
| id | NomeCliente   | RegiaoCliente | DataCadastro | CreditCliente |
+----+---------------+---------------+--------------+---------------+
| 10 | Chloe Gibbs   | CE            | 2016-05-11   |          8165 |
| 14 | Winter Carson | CE            | 2016-12-10   |          2524 |
| 53 | Alea Byrd     | CE            | 2016-02-11   |          5592 |
| 76 | Taylor Ingram | CE            | 2016-06-19   |         15843 |
| 94 | Tanya Barr    | CE            | 2016-01-20   |          3252 |
+----+---------------+---------------+--------------+---------------+

Agora veja a soma de todos os seus créditos:

SELECT SUM(creditCliente) AS "Créditos CE" FROM clientesEstaduais WHERE RegiaoCliente = 'CE';
+--------------+
| Créditos CE  |
+--------------+
|        35376 |
+--------------+

Para ver todos os créditos referentes a um mês (de qualquer dia e qualquer ano):

SELECT SUM(creditCliente) AS "Créditos de Agosto" FROM clientesEstaduais WHERE MONTH(DataCadastro)='08';
+---------------------+
| Créditos de Agosto  |
+---------------------+
|              134245 |
+---------------------+