VB.net / SQL Server

diog0silva

Power Member
Boas.

Tenho uma dúvida. Tenho um campo numa tabela que é NUMERIC (3,1). Ou seja aceita valores 30,1 ou 45,7, etc.

Acontece que quando tou a inserir um valor nesse campo através do Visual Basic, ele dá-me sempre erro.

ja tentei

INSERT INTO tabela (cod_produto, preco) VALUES (20, 12.1)
INSERT INTO tabela (cod_produto, preco) VALUES (20, 12,1)
INSERT INTO tabela (cod_produto, preco) VALUES (20, '12.1')
INSERT INTO tabela (cod_produto, preco) VALUES (20, '12,1')

Dá sempre erro. Quando não meto as pelicas e meto a virgula ele assume três campos. Quando não meto as pelicas e meto o ponto, ele ignora o ponto (121).

Isto quando insiro através do VB!

Cumprimentos.

EDIT: Já consegui.

Se alguém tiver a mesma duvida terá que usar a função replace. Ou seja, se o valor do preço tiver no campo txtPreco.text, e o código do produto no campo txtCod.text, ficará assim:

"INSERT INTO tabela VALUES (" & txtCod.text & "," & replace(Cdbl(txtPreco.text), ",", ".") & ");"
 
Última edição:
O Trim apenas te come os "leading spaces", de uma String.

Acho que nem precisavas do replace. Não testei, mas acho que se enviares um double para um campo numeric, é papado.
Enfim, já resolveste e isso é que interessa ;)
 
JPaulino, tive a dar uma vista de olhos nesses links e vao-me ser muito uteis. Porém, não falam de uma coisa que eu preciso de fazer.

Tipo, quero criar um formulário (com textbox's e não com datagridview) que vá buscar os dados a uma determinada tabela. E queria programar botoes para percorrer os vários registos (tipo anterior, seguinte, procurar).

Conheces algum outro link que possas aconselhar? Se for do genero deste, é optimo, porque está tudo mesmo bem explicado.

Obrigado pela vossa ajuda.
 
O Trim apenas te come os "leading spaces", de uma String.

Acho que nem precisavas do replace. Não testei, mas acho que se enviares um double para um campo numeric, é papado.
Enfim, já resolveste e isso é que interessa ;)

Podes utilizar um BindingNavigator. Estes são alguns artigo mas existem bastantes se procurares por BindingNavigator.

http://www.developer.com/net/vb/article.php/3558771
http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingnavigator.aspx
http://msdn.microsoft.com/en-us/library/3y4w3c32(VS.85).aspx


Vou aproveitar este post e fazer uma pergunta que me anda a incomodar a algum tempo.

Porque tanta obsessão com os SqlParameters?


Os SqlParameters ou OleDbParameters não são propriamente uma obsessão mas sim uma facilidade para o programador. Eles evitam e resolvem diversos problemas que se tem durante o desenvolvimento. Alguns exemplos de vantagens:

Organização
É mais fácil construir um comando T-SQL do que concatenar uma string, mesmo usando um StringBuilder ou o String.Format(). Por exemplo:

"INSERT INTO myTable (id,nome,morada,localidade,codpostal) VALUES (@id,@nome,@morada,@localidade,@codpostal)"

E depois é só definir os parameters linha a linha do que fazer:

"INSERT INTO myTable (id,nome,morada,localidade,codpostal) VALUES (" & Me.TextBox1.Text ",'" & Me.TextBox2.Text & "','" & Me.TextBox3.Text & "','" Me.TextBox4.Text & "'," & Me.TextBox5.Text & ")"

Agora imagina se são 30 ou 40 campos a inserir.

Evitar Erros
Usando os parameters apenas necessitamos de indicar o tipo de dados e o parameter, não sendo necessário conversões, substituições, etc.

Por exemplo numa data temos de especificar o formato correcto, nos números tempos de verificar se é virgulas ou pontos, num nome do tipo O'brien temos de substituir a ' por ", etc. Agora imagina que mudas de país ... a formatação é diferente e tens de recorrer ao namespace Globalization para acertar tudo.

Um conjunto de operações que não são necessárias usando parameters.

Segurança
Os parameters, em conjunto com os stored procedures, evitam os SQL Injections que são umas das principais causas de segurança em aplicações web. Podes ler um artigo que escrevi e onde tem um video que mostra como é simples fazer um ataque em aplicações não seguras:

Artigo: VB.NET: Utilizando Stored Procedures



São alguns exemplos das vantagens de utilizarmos parameters.
 
jpaulino, há alguma maneira de ver o código que está nos bindingnavigators? é que não gosto muito deles, mas queria por um botão "anterior" e "seguinte".

E já agora, se puderes, explica-me para que serve esta parte do código do link que deste (parte 1):

Dim x As Integer = command.ExecuteNonQuery()
If x < 1 Then
MsgBox("A operação efectuada não retomou qualquer resultado.")
End if

E já agora se me puderes explicar a diferença entre ExecuteScalar e ExecuteNonQuery lol
 
Última edição:
jpaulino, há alguma maneira de ver o código que está nos bindingnavigators? é que não gosto muito deles, mas queria por um botão "anterior" e "seguinte".

O BindingNavigator é uma maneira simples de navegar nos registos e usa uma BindingSource. Podes utilizar uma BindingSource apenas e não precisas daqueles botões

http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.movenext.aspx

E já agora, se puderes, explica-me para que serve esta parte do código do link que deste (parte 1):

Dim x As Integer = command.ExecuteNonQuery()
If x < 1 Then
MsgBox("A operação efectuada não retomou qualquer resultado.")
End if

O que está a fazer é executar um comando transact SQL (Insert, Delete ou Update) e verifica o número de registos que foi afectado. Depois verifica o resultado e mostra uma mensagem a indicar que não inseriu, não apagou ou não actualizou. Só isso!

E já agora se me puderes explicar a diferença entre ExecuteScalar e ExecuteNonQuery lol

O ExecuteScalar retorna um resultado e é útil para ler informação de um só campo, como por exemplo um total de registos que está na base de dados. Um ExecuteNonQuery executa um comando e devolve o número de registos afectados.
 
Obrigadão! =)

Já agora, já vi que percebes muito do assunto, queria por-te mais uma questão.

Tou a usar Parameters como recomendaste. E tipo, eu tenho um form com vários campos que correspondem aos vários campos da tabela. Mas nem todos são de preenchimento obrigatório.

Imagina que tenho os campos cliente_cod, cliente_nome, cliente_contacto, cliente_tipo (em que só código e nome do cliente são obrigatórios de preecher, e que o tipo só pode ser "Bom" ou "Mau").

Se eu no form só preencher o campo código e nome, dá erro, porque ele assume que os outros campos têm valor " ". Ou seja, o contacto só aceita valores numericos, e o erro que ele dá é que não consegue converter " " para Integer. Quanto ao tipo, diz que tou a violar uma regra, já que só posso por ou "Bom" ou "Mau".

Tens alguma ideia de como possa resolver isto?

Obrigado mais uma vez.

EDIT:

Eu sei uma maneira, mas além de muito trabalhosa, escrevo mil linha de codigo lol

Do genero, eu tou a fazer:

INSERT INTO cliente VALUES (@cod_cliente, @nome_cliente, @contacto_cliente, @tipo_cliente)

cmd.Parameters.Add... Value = txtCodCliente.text
cmd... Value = txtNomeCliente.text
...

Ou seja, ponho todos os campos. Claro que posso por um IF, e se os campos X ou Y forem igual a " ", faço um INSERT diferente só com os campos que não são iguais a " ".

Mas além desta solução, nao conheces outra mais prática?
 
Última edição:
Imagina que tenho os campos cliente_cod, cliente_nome, cliente_contacto, cliente_tipo (em que só código e nome do cliente são obrigatórios de preecher, e que o tipo só pode ser "Bom" ou "Mau").

Se eu no form só preencher o campo código e nome, dá erro, porque ele assume que os outros campos têm valor " ". Ou seja, o contacto só aceita valores numericos, e o erro que ele dá é que não consegue converter " " para Integer. Quanto ao tipo, diz que tou a violar uma regra, já que só posso por ou "Bom" ou "Mau".

Validação de campos, SEMPRE.
O que vai para a base de dados já tem de ir formatado.
Bindingnavs são bons para complicar processos que são já de si bastante simples.
Quando queres abanar um pouco a fórmula está o caldo entornado.
Faz me lembrar aquilo que um hardcore user de W98 sente ao sentar-se à frente de um WVista. Sente uma brutal perda de controlo sobre o que está a fazer. Chega a ser frustrante.
Um freshman, com o vista não aprende squat. Quase que se faz perder a noção de ficheiro.
Na minha opinião há que fazer um pequeno esforço por entender as operações básicas.
Acabam por fazer as coisas e ficar a saber o mesmo, o que não é bom.
E olha que há bons exemplos, não só no forum mas também aí no blog do jpaulino.
Boas explicações sim senhor :)
 
É como o ribeiro55 já disse, é sempre necessário fazer validações e é muito importante ir lendo BEM as informações que vão sendo dadas. Não se pode simplesmente fazer por fazer, é preciso entender como as coisas funcionam

Então como é que faço para que cada vez que uma textbox não seja preenchida ele assuma que o valor da mesma é NULL e não " " ?

Só tens de verificar se o campo está em branco e fazer isto:

Código:
If Me.TextBox1.Text = String.Empty Then
     cmd.Parameters(..). Value = DbNull.Value
Else
     cmd.Parameters(..). Value = Me.TextBox1.Text 
End If
 
Obrigado jpaulino, resultou =)

Mas tenho outra dúvida. O form para inserir registos já está a funcionar na perfeição. O problema agora é ler registos, fazer seguinte, anterior, procurar, etc. Tive a ler o teu blogue e tive-me a guiar por lá, mas o exemplo que está lá não lê os dados da base de dados para textbox's, que era o que eu queria fazer.

Até agora, tenho o código assim:

Const constr As String = "Data Source....."
Dim db As New SqlConnection(constr)

db.Open()

Dim varsql As String
varsql = "SELECT * FROM tab_cliente ORDER BY clt_nome"

Dim cmd As New SqlCommand (varsql, db)
Dim reader As SqlDataReader

reader = cmd.ExecuteReader
reader.Read()

txtCodCliente = reader.Item("clt_cod")
txtNomeCliente = reader.Item("clt_nome")
txtClienteTipo = reader.Item("clt_tipo")
...

db.Close()

De facto, consigo ler no form os dados do registo do primeiro cliente. Agora, gostava de saber se é esta a maneira correcta de fazer a leitura da base de dados, e se sim, como programo os botões anterior e seguinte?

EDIT: desta maneira tem uma falha; se o campo for NULL, ele bloqueia lol
 
Última edição:
Obrigado jpaulino. Já conhecia as stored procedures e tudo o resto, so me escapava a noção do SqlInjection e da globalização. Agora sim entendo o porque de usar os parametros mesmo sem usar stored procedures.

Diog0Silva, nesse formulário onde estás a ler os dados, apenas faz isso certo? ler os dados e + nada?
 
Sim. Aquele código que pus aqui le os dados do primeiro registo da tabela. Esse form só vai servir para ler os dados, fazer seguinte, anterior, fazer procuras, alterar e apagar dados.

Apagar e alterar eu acho que consigo. Ler, navegar entre os registos e procurar um registo é que está mais complicado.

Tenho procurado em vários sites e livros, mas os exemplos que aparecem são com console application.
 
Eu ia-te a sugerir para usares uma DataTable em vez de um DataReader mas para o que queres sei que a formas mais simples, apesar de não conseguires ter tanto controlo como com a DataTable.

Basicamente quando usas uma datatable ficas com uma tabela tal e qual como está no sql em memoria que podes usar para mostrar dados mas sempre que fizeres alguma alteração a tabela, tens que passar essa alteração para o Sql.

Um exemplo de como meter os dados numa Datatable:

Código:
[COLOR="YellowGreen"]'Este exemplo assume que tens uma variavel do tipo SqlConnection chamada SqlCon com os 
'dados da ligação a base de dados[/COLOR]
Dim SqlCmd as new SqlCommand("Select * from TabelaXpto", SqlCon)
Dim SqlAdp As New SqlDataAdapter(SqlCmd)

Dim Dt As New DataTable

SqlAdp.Fill(Dt)

Depois podes navegar os dados internamente. Para colocar dados numa textbox:

Código:
Textbox1.Text = Dt.Rows(0).Item("Coluna1").ToString
Textbox2.Text = Dt.Rows(0).Item("Coluna2").ToString

No exemplo, tens que ter em atenção o .rows(0) onde o 0 é o numero da linha (Atenção que é zero-based index, ou seja o index começa no 0 em vez de começar no 1) e o .Item("Coluna1") que é a coluna de onde queres retirar os dados. Também podes colocar .Item(0) que devolve-te os dados da primeira coluna mas convem usares sempre os nomes.

Quando queres alterar ou eliminar dados, tens que executar a query para o mesmo no Sql, e depois ir buscar novamente a tabela para actualizares os dados.
 
Obrigado PNDmartins, funciona dessa maneira!

Diz-me só uma coisa: alguma razão em especial para se usar o ToString?

EDIT: ya, acho q já percebi a utilidade do ToString lol se não converter para string, ele bloqueia se existirem campos NULL.

Já agora, ponho nova questão lol

Não consigo eliminar "clientes", porque têm "compras" efectuadas. Ou seja, quero eliminar um registo numa tabela, mas como esse registo está a ser utilizado noutra tabela ele não deixa. Alguma solução? É mesmo para apagar o registo e todas as suas incidências noutras tabelas.
 
Última edição:
Apenas para ter a certeza de que os valores que vêem da dase de dados são convertidos para string para dar ao texto da textbox, visto que a mesma é do tipo string.

Eu programo com a opção "OptionStrick" ligada, o que me obriga a ter sempre os valores convertidos das maneiras correctas, para minimizar os erros não vá eu enganar-me quando estou a programar e querer colocar por exemplo:

Código:
Dim AData as Date = "Omg, este é o tipo errado e o programa vai arrebentar."

Dai habituei-me a converter sempre os dados para os tipos correctos. Não necessitas colocar o .tostring mas é uma mais valia.

Também é um bom habito converter sempre os dados para os tipos correctos.

EDIT: Tinha-me esquecido dos valores nullos como desde a um tempo para cá ando a habituar-me a utilizar o .tostring, mas sim, também evita isso.
 
Última edição:
Back
Topo