I have a table my_table in a database my_db with a String column named IpAddr.
The following query:
select
IpAddr,
lower(IpAddr),
IpAddr = lower(IpAddr)
from my_db.my_table
where IpAddr = '10.144.145.159'
limit 1
produces the following output:
IpAddr lower(IpAddr) equals(IpAddr, lower(IpAddr))
10.144.145.159 10.144.145.159 1
The question is how may I interpret that number 1 as the result of the comparision?
Does it mean that the String and its lowered counterpart are not the same? Or is it exactly the oposite?
Just found out that 1 means true, because the following query:
select
IpAddr,
name,
IpAddr = name
from my_db.my_table
where IpAddr = '10.144.145.159'
limit 1
IpAddr Name equals(IpAddr, Name)
10.144.145.159 myName 0
the equals returns 0 for known false case, this is, because 10.144.145.159 and myName are not equal
Related
I have a Scala Function. I need to read records that their last two characters of ID column are equal to the result of func. So, I must use func in Oracle query which is in following:
def func(id:String): Int = {
val two_char = id.takeRight(2).toInt
val group_id = two_char % 4
return group_id
}
val query = """ SELECT * FROM table where """+func("ID") + """==="""+group_id
When Scala run the query, I receive this error:
Exception in thread "main" java.lang.NumberFormatException: For input string: "ID"
func pass name of column not its values. Would you please guide me how to use the function in Oracle Query to operate on each value of the column?
Any help is really appreciated.
I'm trying to scan the result of a query into a res structure.
The code builds and the query passes but the result array consists of default values like this:
[{0 0 0} {0 0 0} {0 0 0} {0 0 0} {0 0 0} {0 0 0}]
Also, result array has the exact length as the query result should have.
When i try generated query in postgres shell it returns the result correctly.
Code:
type res struct{
id int
number int
user_id int
}
func getDataJoin(){
new := []res{}
db.Db.Table("users").Select("users.id as id, credit_cards.number as number, credit_cards.user_id as user_id").Joins("left join credit_cards on credit_cards.user_id = users.id").Scan(&new)
fmt.Println("user\n",new)
}
Generated Query:
SELECT users.id as id, credit_cards.number as number, credit_cards.user_id as user_id FROM "users" left join credit_cards on credit_cards.user_id = users.id
Database result
id | number | user_id
----+--------+---------
1 | 1 | 1
1 | 2 | 1
2 | 1 | 2
2 | 2 | 2
3 | 1 | 3
3 | 2 | 3
(6 rows)
Since go-gorm has a certain convention when it comes to naming, you might want to try two things.
Make your res struct publicly available, with public fields:
type Res struct{
ID int
Number int
UserID int
}
Or, specify mappings between columns and fields:
type res struct{
id int `gorm:"column:id"`
number int `gorm:"column:number"`
user_id int `gorm:"column:user_id"`
}
gorm can only read/write on exported fields much like Marshal/Unmarshal methods of json package. If the first letter of your field is in capital, it will be used. By default, gorm matches struct fields with their camel-cased forms. You can also define your own column names.
Since camel-cased form of both ID and Id, is id, as long as the first letter of your field is in capital, it should work. On a different note, it's good practice to write ID, i.e., both letter capital.
I need to create a sql query :
SELECT * FROM users WHERE id = 10 AND name = "Chetan"
Now, gorm's where function looks like below,
// Where return a new relation, filter records with given conditions, accepts `map`, `struct` or `string` as conditions, refer http://jinzhu.github.io/gorm/crud.html#query
func (s *DB) Where(query interface{}, args ...interface{}) *DB {
return s.clone().search.Where(query, args...).db
}
Which mean it accepts a query and args. Example :
dbDriver.Where("id = ?", id).First(t)
How do i dynamically pass multiple variables. Example:
SELECT * FROM users WHERE id = 10 AND name = "Chetan"
SELECT * FROM users WHERE id = 10
SELECT * FROM users WHERE gender = "male" AND name = "Chetan" AND age = "30"
Is it poosible to write a single gorm function for such dynamic SQL statements?
You can use map[string]interface{} for coditions in .Where()
m := make(map[string]interface{})
m["id"] = 10
m["name"] = "chetan"
db.Where(m).Find(&users)
Just add your conditions in map then send inside where.
Or you can use struct in .Where(). Create a variable of struct and set those field for which you want to query and send inside where
db.Where(&User{Name: "chetan", Gender: "Male"}).First(&user)
NOTE: When query with struct, GORM will only query with those fields has non-zero value, that means if your field’s value is 0, '', false or other zero values, it won’t be used to build query conditions.
Referrence: https://gorm.io/docs/query.html#Struct-amp-Map
The first param of .Where() accepts string and the rest is variadic, this means you have the capability to modify the query and the values.
In the below example, I've prepared field1 & field2, and also value1 & value2 for representing the names of the fields I want to filter and their values respectively.
The values can be in any type since it's interface{}.
var field1 string = "id"
var value1 interface{} = 10
var field2 string = "age"
var value2 interface{} = "30"
dbDriver.Where(field1 " = ? AND " + field2 + " = ?", value1, value2).First(t)
Update 1
What if i am not sure what are number of parameter i will be passing? In this case we are hardcoding to two. What if that function/method passes 3 ?
One possible solution to achieve that, is by using slices to hold the criteria. You will have the control to dynamically adjust the fields and values.
fields := []string{"id = ?", "age = ?"}
values := []interface{}{10, "30"}
dbDriver.Where(strings.Join(fields, " AND "), values...).First(t)
Update 2
As per #Eklavya's comment, it's also possible to use a predefined struct object or a map instead of a string on the where clause.
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
Reference: GORM query reference
I'm trying to create a dictionary from http://standards-oui.ieee.org/oui/oui.csv
With this code:
CREATE DICTIONARY TestDict (
registry String DEFAULT '',
assignment String DEFAULT '',
name String DEFAULT '',
address String DEFAULT ''
)
PRIMARY KEY assignment
SOURCE(HTTP(
url 'http://standards-oui.ieee.org/oui/oui.csv'
format 'CSVWithNames'
))
LAYOUT(FLAT())
LIFETIME(300)
But when I try to select * from default.TestDict it returns error "Table default.testDict doesn't exist". And then dictionary status turns to "FAILED".
What am I doing wrong?
First let's look at dictionary last_exception-param:
SELECT *
FROM system.dictionaries
FORMAT Vertical
/*
..
last_exception: Code: 27, e.displayText() = DB::Exception: Cannot parse input: expected , before: MA-L,002272,American Micro-Fuel Device Corp.,2181 Buchanan Loop Ferndale WA US 98248 \r\nMA-L,00D0EF,IGT,9295 PROTOTYPE DRIVE RENO NV US 89511 \r\nMA-L,086195,Rockw: (at row 1)
Row 1:
Column 0, name: assignment, type: UInt64, ERROR: text "MA-L,00227" is not like UInt64
..
*/
The reason for the problem in the wrong selection of layout: FLAT-layout expects the numeric primary key with type UInt64 instead of String.
It needs to use composite key that support String-type and related COMPLEX_KEY_HASHED-layout.
CREATE DICTIONARY TestDict
(
registry String DEFAULT '',
assignment String DEFAULT '',
name String DEFAULT '',
address String DEFAULT ''
)
PRIMARY KEY registry, assignment, name
SOURCE(HTTP(URL 'http://standards-oui.ieee.org/oui/oui.csv' FORMAT CSVWithNames))
LIFETIME(MIN 0 MAX 300)
LAYOUT(COMPLEX_KEY_HASHED())
Take into account that primary key consists of three columns (registry, assignment, name) to uniquely identify each row.
SELECT count()
FROM default.TestDict
/* result
┌─count()─┐
│ 27742 │
└─────────┘
*/
I have a SQL query with a WHERE clause that typically has values including a dash as stored in the database as a CHAR(10). When I explicitly call it like in the following:
$sth = $dbh->prepare("SELECT STATUS_CODE FROM MyTable WHERE ACC_TYPE = 'A-50C'");
It works and properly returns my 1 row; however if I do the following:
my $code = 'A-50C';
$sth = $dbh->prepare("SELECT STATUS_CODE FROM MyTable WHERE ACC_TYPE = ?");
$sth->execute($code);
or I do:
my $code = 'A-50C';
$sth = $dbh->prepare("SELECT STATUS_CODE FROM MyTable WHERE ACC_TYPE = ?");
$sth->bind_param(1, $code);
$sth->execute();
The query completes, but I get no results. I suspect it has to do with the dash being interpretted incorrectly, but I can't link it to a Perl issue as I have printed my $code variable using print "My Content: $code\n"; so I can confirm its not being strangely converted. I also tried including a third value for bind_param and if I specify something like ORA_VARCHAR2, SQL_VARCHAR (tried all possibilities) I still get no results. If I change it to the long form i.e. { TYPE => SQL_VARCHAR } it gives me an error of
DBI::st=HASH<0x232a210>->bind_param(...): attribute parameter
'SQL_VARCHAR' is not a hash ref
Lastly, I tried single and double quotes in different ways as well as back ticks to escape the values, but nothing got me the 1 row, only 0. Any ideas? Haven't found anything in documentation or searching. This is oracle for reference.
Code with error checking:
my $dbh = DBI->connect($dsn, $user, $pw, {PrintError => 0, RaiseError => 0})
or die "$DBI::errstr\n";
# my $dbh = DBI->connect(); # connect
my $code = 'A-50C';
print "My Content: $code\n";
$sth = $dbh->prepare( "SELECT COUNT(*) FROM MyTable WHERE CODE = ?" )
or die "Can't prepare SQL statement: $DBI::errstr\n";
$sth->bind_param(1, $code);
$sth->execute() or die "Can't execute SQL statement: $DBI::errstr\n";
my $outfile = 'output.txt';
open OUTFILE, '>', $outfile or die "Unable to open $outfile: $!";
while(my #re = $sth->fetchrow_array) {
print OUTFILE #re,"\n";
}
warn "Data fetching terminated early by error: $DBI::errstr\n"
if $DBI::err;
close OUTFILE;
$sth->finish();
$dbh->disconnect();
I ran a trace and got back:
-> bind_param for DBD::Oracle::st (DBI::st=HASH(0x22fbcc0)~0x3bcf48 2 'A-50C' HASH(0x22fbac8)) thr#3b66c8
dbd_bind_ph(1): bind :p2 <== 'A-50C' (type 0 (DEFAULT (varchar)), attribs: HASH(0x22fbac8))
dbd_rebind_ph_char() (1): bind :p2 <== 'A-50C' (size 5/16/0, ptype 4(VARCHAR), otype 1 )
dbd_rebind_ph_char() (2): bind :p2 <== ''A-50' (size 5/16, otype 1(VARCHAR), indp 0, at_exec 1)
bind :p2 as ftype 1 (VARCHAR)
dbd_rebind_ph(): bind :p2 <== 'A-50C' (in, not-utf8, csid 178->0->178, ftype 1 (VARCHAR), csform 0(0)->0(0), maxlen 16, maxdata_size 0)
Your problem is likely a result of comparing CHAR and VARCHAR data together.
The CHAR data type is notorious (and should be avoided), because it stores data in fixed-length format. It should never be used for holding varying-length data. In your case, data stored in the ACC_TYPE column will always take up 10 characters of storage. When you store a value whose length is less than the size of the column, like A-50C, the database will implicitly pad the string up to 10 characters, so the actual value stored becomes A-50C_____ (where _ represents a whitespace).
Your first query works because when you use a hard-code literal, Oracle will automatically right-pad the value for you (A-50C -> A-50C_____). However, in your second query where you use bind variables, you're comparing a VARCHAR against a CHAR and no auto-padding will happen.
As a quick fix to the problem, you could add right-padding to the query:
SELECT STATUS_CODE FROM MyTable WHERE ACC_TYPE = rpad(?, 10)
A long-term solution would be to avoid using the CHAR data type in your table definitions and switch to VARCHAR2 instead.
As your DBI_TRACE revealed, the ACC_TYPE column is CHAR(10) but the bound parameter is understood as VARCHAR.
When comparing CHAR, NCHAR, or literal strings to one another, trailing blanks are effectively ignored. (Remember, CHAR(10) means that ACC_TYPE values are padded to 10 characters long.) Thus, 'A ' and 'A', as CHARs, compare equal. When comparing with the VARCHAR family, however, trailing blanks become significant, and 'A ' no longer equals 'A' if one is a VARCHAR variant.
You can confirm in sqlplus or via quick DBI query:
SELECT COUNT(1) FROM DUAL WHERE CAST('A' AS CHAR(2)) = 'A'; -- or CAST AS CHAR(whatever)
SELECT COUNT(1) FROM DUAL WHERE CAST('A' AS CHAR(2)) = CAST('A' AS VARCHAR(1));
(Oracle terms these blank-padded and nonpadded comparison semantics, since the actual behavior per the ANSI-92 spec is to pad the shorter CHAR or literal to the length of the longer and then compare. The effective behavior, whatever the name, is that one ignores trailing blanks and the other does not.)
As #MickMnemonic suggested, RPAD()ing the bound value will work, or, better, altering the column type to VARCHAR. You could also CAST(? AS CHAR(10)). TRIM(TRAILING FROM ACC_TYPE) would work, too, at the cost of ignoring any index on that column.