SQL과 값 바인딩
Unibase 소스 안에는 SQL 문장 표현식을 작성할 수 있습니다. SQL을 문자열 연결로 만들기보다 소스의 값을 문장에 전달하는 구조를 살펴봅니다. 두 소스는 실행할 때 테이블을 만들고 실제 Unibase DB에 값을 저장·조회합니다. 빌드·실행을 누르면 저장 뒤 읽은 값과 빈 조회 결과를 확인할 수 있습니다. 예제마다 독립된 임시 DB를 사용하므로 이전 실행의 데이터가 다음 결과에 섞이지 않습니다.
다른 언어의 탭에서는 값 바인딩·행 조회·빈 결과 처리를 비교할 수 있도록 SQLite 메모리 DB를 사용합니다. Java는 SQLite JDBC 드라이버, C#은 Microsoft.Data.Sqlite, Go는 modernc.org/sqlite 의존성이 필요합니다. Python은 표준 sqlite3, TypeScript는 Node.js의 node:sqlite를 사용합니다. Unibase의 SQL 문장 표현식은 원본 탭에서 그대로 확인할 수 있습니다.
INSERT에 값 전달하기
섹션 제목: “INSERT에 값 전달하기”_name은 소스의 문자열 변수이며 VALUES(_name)에서 사용합니다. _sql의 오른쪽은 큰따옴표로 감싼 문자열이 아니라 SQL 문장 표현식입니다.
class SqlInsertBinding {
static void main() {
CREATE TABLE example_users (id INT, name TEXT);
var _name = "Kim";
var _sql = INSERT INTO example_users (id, name) VALUES(1, _name);
var result = _sql.execute();
ResultSet rows = SELECT name FROM example_users WHERE id = 1;
print(rows.rowCount());
print(rows.getString(0, "name"));
}
}import java.sql.*;
import java.util.*;
public class SqlInsertBinding {
public static void main(String[] args) throws Exception {
try (Connection db = DriverManager.getConnection("jdbc:sqlite::memory:")) {
try (Statement statement = db.createStatement()) {
statement.executeUpdate("CREATE TABLE example_users(id INTEGER, name TEXT)");
}
String name = "Kim";
try (PreparedStatement statement = db.prepareStatement("INSERT INTO example_users(id, name) VALUES (?, ?)")) {
statement.setInt(1, 1);
statement.setString(2, name);
statement.executeUpdate();
}
List<String> rows = queryNames(db, 1);
System.out.println(rows.size());
System.out.println(rows.get(0));
}
}
static List<String> queryNames(Connection db, int id) throws SQLException {
try (PreparedStatement statement = db.prepareStatement("SELECT name FROM example_users WHERE id = ?")) {
statement.setInt(1, id);
try (ResultSet result = statement.executeQuery()) {
List<String> rows = new ArrayList<>();
while (result.next()) { rows.add(result.getString("name")); }
return rows;
}
}
}
}SQLite 메모리 DB의 JDBC 대응 예시입니다. 실행 클래스 경로에 org.xerial:sqlite-jdbc 드라이버가 필요합니다.
import sqlite3
db = sqlite3.connect(":memory:")
try:
db.execute("CREATE TABLE example_users(id INTEGER, name TEXT)")
name = "Kim"
db.execute("INSERT INTO example_users(id, name) VALUES (?, ?)", (1, name))
rows = db.execute("SELECT name FROM example_users WHERE id = ?", (1,)).fetchall()
print(len(rows))
print(rows[0][0])
finally:
db.close()표준 sqlite3로 SQLite 메모리 DB를 사용합니다. Unibase의 SQL 문장 표현식은 이 탭에서 매개변수가 있는 SQL 문자열에 대응합니다.
using System;
using Microsoft.Data.Sqlite;
using System.Collections.Generic;
class SqlInsertBinding {
static void Main() {
using var db = new SqliteConnection("Data Source=:memory:");
db.Open();
using (var create = db.CreateCommand()) {
create.CommandText = "CREATE TABLE example_users(id INTEGER, name TEXT)";
create.ExecuteNonQuery();
}
string name = "Kim";
using (var statement = db.CreateCommand()) {
statement.CommandText = "INSERT INTO example_users(id, name) VALUES ($id, $name)";
statement.Parameters.AddWithValue("$id", 1);
statement.Parameters.AddWithValue("$name", name);
statement.ExecuteNonQuery();
}
List<string> rows = QueryNames(db, 1);
Console.WriteLine(rows.Count);
Console.WriteLine(rows[0]);
}
static List<string> QueryNames(SqliteConnection db, int id) {
using var statement = db.CreateCommand();
statement.CommandText = "SELECT name FROM example_users WHERE id = $id";
statement.Parameters.AddWithValue("$id", id);
using var result = statement.ExecuteReader();
var rows = new List<string>();
while (result.Read()) { rows.Add(result.GetString(0)); }
return rows;
}
}SQLite 메모리 DB의 ADO.NET 대응 예시입니다. 프로젝트에 Microsoft.Data.Sqlite 패키지가 필요합니다.
import { DatabaseSync } from "node:sqlite";
const db = new DatabaseSync(":memory:");
try {
db.exec("CREATE TABLE example_users(id INTEGER, name TEXT)");
const name = "Kim";
db.prepare("INSERT INTO example_users(id, name) VALUES (?, ?)").run(1, name);
const select = db.prepare("SELECT name FROM example_users WHERE id = ?");
const rows = select.all(1);
console.log(rows.length);
console.log(rows[0].name);
} finally { db.close(); }Node.js 22.12 이상에서 node:sqlite로 SQLite 메모리 DB를 사용합니다. 브라우저의 TypeScript 실행 예시가 아닙니다.
package main
import (
"database/sql"
"fmt"
_ "modernc.org/sqlite"
)
func check(err error) {
if err != nil {
panic(err)
}
}
func queryNames(db *sql.DB, id int) []string {
result, err := db.Query("SELECT name FROM example_users WHERE id = ?", id)
check(err)
defer result.Close()
var rows []string
for result.Next() {
var name string
check(result.Scan(&name))
rows = append(rows, name)
}
check(result.Err())
return rows
}
func main() {
db, err := sql.Open("sqlite", ":memory:")
check(err)
defer db.Close()
db.SetMaxOpenConns(1)
_, err = db.Exec("CREATE TABLE example_users(id INTEGER, name TEXT)")
check(err)
name := "Kim"
_, err = db.Exec("INSERT INTO example_users(id, name) VALUES (?, ?)", 1, name)
check(err)
rows := queryNames(db, 1)
fmt.Println(len(rows))
fmt.Println(rows[0])
}database/sql과 modernc.org/sqlite 드라이버로 SQLite 메모리 DB를 사용합니다. 모듈에 modernc.org/sqlite 의존성이 필요합니다.
다른 언어의 탭은 직접 작성한 대응 코드이며 컴파일러의 자동 생성 결과가 아닙니다. 언어마다 API와 실행 환경이 다릅니다. 이 페이지의 빌드·실행은 Unibase 탭에서 사용할 수 있습니다.
Unibase 실행 결과
빌드·실행 메시지
출력 비교 기준
1
Kim
검증 상태: 문법 검사 통과 · 실행 출력 일치 (2026-10-06)
읽기 기준 결과와 실제 실행 출력 비교
코드를 읽어 정한 비교 기준입니다. 실행 성공 결과로 대신 표시하지 않습니다.
1
Kim실제 네이티브 실행: 빌드 passed, 실행 passed, 종료 코드 0
1
Kim현재 실행 확인 범위CREATE TABLE은 id·name 열을 준비합니다. _sql은 INSERT 문장을 보관하고 execute()에서 수행합니다. 그 뒤 SELECT로 다시 읽은 행 수 1과 저장 값 Kim을 출력합니다. 변수 이름만으로 모든 SQL 바인딩 방식이 같다고 일반화하지 않습니다.
SELECT 결과 읽기
섹션 제목: “SELECT 결과 읽기”_id를 조건에 사용하고 결과를 ResultSet에 받는 형태입니다. getString(0, "name")은 첫 행의 name 값을 읽는 호출입니다.
class SqlSelectBinding {
static void main() {
CREATE TABLE example_users (id INT, name TEXT);
INSERT INTO example_users (id, name) VALUES(1, 'Kim');
int _id = 1;
ResultSet rows = SELECT name FROM example_users WHERE id = _id;
string name = rows.getString(0, "name");
print(name);
ResultSet empty = SELECT name FROM example_users WHERE id = 99;
print(empty.rowCount());
print(empty.isEmpty());
}
}import java.sql.*;
import java.util.*;
public class SqlSelectBinding {
public static void main(String[] args) throws Exception {
try (Connection db = DriverManager.getConnection("jdbc:sqlite::memory:")) {
try (Statement statement = db.createStatement()) {
statement.executeUpdate("CREATE TABLE example_users(id INTEGER, name TEXT)");
}
String name = "Kim";
try (PreparedStatement statement = db.prepareStatement("INSERT INTO example_users(id, name) VALUES (?, ?)")) {
statement.setInt(1, 1);
statement.setString(2, name);
statement.executeUpdate();
}
List<String> rows = queryNames(db, 1);
System.out.println(rows.get(0));
List<String> empty = queryNames(db, 99);
System.out.println(empty.size());
System.out.println(empty.isEmpty());
}
}
static List<String> queryNames(Connection db, int id) throws SQLException {
try (PreparedStatement statement = db.prepareStatement("SELECT name FROM example_users WHERE id = ?")) {
statement.setInt(1, id);
try (ResultSet result = statement.executeQuery()) {
List<String> rows = new ArrayList<>();
while (result.next()) { rows.add(result.getString("name")); }
return rows;
}
}
}
}SQLite 메모리 DB의 JDBC 대응 예시입니다. 실행 클래스 경로에 org.xerial:sqlite-jdbc 드라이버가 필요합니다.
import sqlite3
db = sqlite3.connect(":memory:")
try:
db.execute("CREATE TABLE example_users(id INTEGER, name TEXT)")
name = "Kim"
db.execute("INSERT INTO example_users(id, name) VALUES (?, ?)", (1, name))
rows = db.execute("SELECT name FROM example_users WHERE id = ?", (1,)).fetchall()
print(rows[0][0])
empty = db.execute("SELECT name FROM example_users WHERE id = ?", (99,)).fetchall()
print(len(empty))
print(str(len(empty) == 0).lower())
finally:
db.close()표준 sqlite3로 SQLite 메모리 DB를 사용합니다. Unibase의 SQL 문장 표현식은 이 탭에서 매개변수가 있는 SQL 문자열에 대응합니다.
using System;
using Microsoft.Data.Sqlite;
using System.Collections.Generic;
class SqlSelectBinding {
static void Main() {
using var db = new SqliteConnection("Data Source=:memory:");
db.Open();
using (var create = db.CreateCommand()) {
create.CommandText = "CREATE TABLE example_users(id INTEGER, name TEXT)";
create.ExecuteNonQuery();
}
string name = "Kim";
using (var statement = db.CreateCommand()) {
statement.CommandText = "INSERT INTO example_users(id, name) VALUES ($id, $name)";
statement.Parameters.AddWithValue("$id", 1);
statement.Parameters.AddWithValue("$name", name);
statement.ExecuteNonQuery();
}
List<string> rows = QueryNames(db, 1);
Console.WriteLine(rows[0]);
List<string> empty = QueryNames(db, 99);
Console.WriteLine(empty.Count);
Console.WriteLine((empty.Count == 0).ToString().ToLowerInvariant());
}
static List<string> QueryNames(SqliteConnection db, int id) {
using var statement = db.CreateCommand();
statement.CommandText = "SELECT name FROM example_users WHERE id = $id";
statement.Parameters.AddWithValue("$id", id);
using var result = statement.ExecuteReader();
var rows = new List<string>();
while (result.Read()) { rows.Add(result.GetString(0)); }
return rows;
}
}SQLite 메모리 DB의 ADO.NET 대응 예시입니다. 프로젝트에 Microsoft.Data.Sqlite 패키지가 필요합니다.
import { DatabaseSync } from "node:sqlite";
const db = new DatabaseSync(":memory:");
try {
db.exec("CREATE TABLE example_users(id INTEGER, name TEXT)");
const name = "Kim";
db.prepare("INSERT INTO example_users(id, name) VALUES (?, ?)").run(1, name);
const select = db.prepare("SELECT name FROM example_users WHERE id = ?");
const rows = select.all(1);
console.log(rows[0].name);
const empty = select.all(99);
console.log(empty.length);
console.log(empty.length === 0);
} finally { db.close(); }Node.js 22.12 이상에서 node:sqlite로 SQLite 메모리 DB를 사용합니다. 브라우저의 TypeScript 실행 예시가 아닙니다.
package main
import (
"database/sql"
"fmt"
_ "modernc.org/sqlite"
)
func check(err error) {
if err != nil {
panic(err)
}
}
func queryNames(db *sql.DB, id int) []string {
result, err := db.Query("SELECT name FROM example_users WHERE id = ?", id)
check(err)
defer result.Close()
var rows []string
for result.Next() {
var name string
check(result.Scan(&name))
rows = append(rows, name)
}
check(result.Err())
return rows
}
func main() {
db, err := sql.Open("sqlite", ":memory:")
check(err)
defer db.Close()
db.SetMaxOpenConns(1)
_, err = db.Exec("CREATE TABLE example_users(id INTEGER, name TEXT)")
check(err)
name := "Kim"
_, err = db.Exec("INSERT INTO example_users(id, name) VALUES (?, ?)", 1, name)
check(err)
rows := queryNames(db, 1)
fmt.Println(rows[0])
empty := queryNames(db, 99)
fmt.Println(len(empty))
fmt.Println(len(empty) == 0)
}database/sql과 modernc.org/sqlite 드라이버로 SQLite 메모리 DB를 사용합니다. 모듈에 modernc.org/sqlite 의존성이 필요합니다.
다른 언어의 탭은 직접 작성한 대응 코드이며 컴파일러의 자동 생성 결과가 아닙니다. 언어마다 API와 실행 환경이 다릅니다. 이 페이지의 빌드·실행은 Unibase 탭에서 사용할 수 있습니다.
Unibase 실행 결과
빌드·실행 메시지
출력 비교 기준
Kim
0
true
검증 상태: 문법 검사 통과 · 실행 출력 일치 (2026-10-06)
읽기 기준 결과와 실제 실행 출력 비교
코드를 읽어 정한 비교 기준입니다. 실행 성공 결과로 대신 표시하지 않습니다.
Kim
0
true실제 네이티브 실행: 빌드 passed, 실행 passed, 종료 코드 0
Kim
0
true현재 실행 확인 범위소스의 첫 두 문장은 테이블과 초기 행을 준비합니다. _id = 1인 조회는 Kim을 얻고, id = 99인 조회는 rowCount()가 0이며 isEmpty()가 true입니다. 빈 결과에서 첫 행을 읽지 않는 흐름이 중요합니다. 운영 DB의 연결·트랜잭션·마이그레이션 절차는 별도 계약으로 다룹니다.
문장, 실행, 결과를 구분하기
섹션 제목: “문장, 실행, 결과를 구분하기”SQL 문장을 표현하는 값, 그 문장을 실행하는 호출, 실행 뒤 얻는 결과는 서로 다른 단계입니다. 문장 소스가 검사됐다고 데이터베이스 연결과 저장까지 확인된 것은 아닙니다.
| 단계 | 본문의 소스에서 찾을 것 |
|---|---|
| 값 준비 | _name, _id의 타입과 값 |
| 문장 표현 | INSERT의 VALUES, SELECT의 WHERE |
| 수행 또는 조회 | execute() 또는 ResultSet을 받는 표현식 |
| 결과 사용 | 조회 행과 열 이름 |
SQL에 값을 전달하는 것과 테이블·열 이름을 문자열로 조립하는 것은 구분해야 합니다. 사용자가 입력한 SQL 조각을 임의 실행하는 기능은 이 문서의 서버 예제에 없습니다.
ResultSet API
섹션 제목: “ResultSet API”현재 ResultSet에는 다음 메서드가 선언되어 있습니다.
| 선언 | 용도 |
|---|---|
int rowCount() |
결과 행 수 조회 |
bool isEmpty() |
비어 있는 결과 조회 |
string getString(int row, string column) |
행 번호와 열 이름으로 문자열 조회 |
getString(0, "name")을 사용하려면 첫 행과 해당 열이 존재해야 합니다. 0은 행 번호이고 "name"은 열 이름이므로 서로 바꾸어 전달할 수 없습니다. 현재 native 경로는 없는 행·열에 런타임 오류를 보고합니다. SQL NULL의 문자열 변환과 타겟별 차이는 별도 검증 범위입니다.
현재 예제와 명세의 표현 차이
섹션 제목: “현재 예제와 명세의 표현 차이”본문은 검증된 native 경로의 직접 SQL 문장 표현식을 사용합니다. _name 같은 소스 변수는 문장의 값 위치에 전달되며 문자열 안의 작은따옴표는 SQL 문자열 규칙에 맞춰 처리합니다. 언어 명세의 sql { ... }와 :name 표현, 외부 DB와의 연동을 같은 지원 범위로 일반화하지 않습니다.
실행 실패를 확인할 때
섹션 제목: “실행 실패를 확인할 때”SQL 구문 오류·없는 테이블·없는 열·잘못된 행 번호는 다른 실패 원인입니다. 먼저 빌드와 실행 중 어느 단계가 실패했는지 읽고 진단을 확인하세요. 프로세스 종료 코드만 확인하지 말고 실제 행 수와 값을 비교합니다. 오류와 예외에서 단계별 실패를 구분하는 방법을 읽을 수 있습니다.