SQLite + FMDB sql语句实现实例
Core Data 在各种性能测试中,表现出来的收益是相当差的。在这一点上,Realm 是最喜欢拿来说事的,拿查阅来说,Core Data 的性能是 FMDB 的六分之一不到,Realm 的十分之一不到。 --唐巧的技术博客http://blog.devtang.com/2016/08/04/i-do-not-like-core-data/
想灵活应用FMDB,必须先了解和熟悉SQLite。这里是对http://www.runoob.com/sqlite/sqlite-tutorial.html 的一些简化,记录。后续还会结合FMDB直接上例子,上代码。
1 // 2 // SQLiteStudy.m 3 // FMDB的二次封装 4 // 5 // Created by baoshan on 16/9/7. 6 // 7 // 8 9 #import "SQLiteStudy.h" 10 #import "DbManager.h" 11 12 /** 13 * SQLite 常用语法,参考资料:http://www.runoob.com/sqlite/sqlite-tutorial.html 14 */ 15 16 @implementation SQLiteStudy{ 17 18 __block FMDatabase *_db; 19 } 20 21 //表名 22 static NSString *const TABLE_NAME_COMPANY = @"COMPANY"; 23 static NSString *const TABLE_NAME_COMPANY_BKP = @"COMPANY_BKP"; 24 static NSString *const TABLE_NAME_DEPARTMENT = @"DEPARTMENT"; 25 static NSString *const TABLE_NAME_ADULT = @"ADULT"; 26 27 #pragma mark - 初始化 28 29 - (instancetype)init 30 { 31 self = [super init]; 32 if (self) { 33 [self configDb]; 34 } 35 return self; 36 } 37 /** 38 * 方便测试 39 */ 40 - (void)configDb{ 41 42 [[DbManager getInstance].dbQueue inDatabase:^(FMDatabase *db) { 43 44 _db = db; 45 46 }]; 47 48 } 49 #pragma mark - 插入数据测试 50 - (void)insertData{ 51 52 NSString *sql = @"INSERT INTO COMPANY (NAME,AGE,ADDRESS,SALARY) VALUES ('Paul', 34, 'California', 20000.00)"; 53 [_db executeUpdate:sql]; 54 } 55 #pragma mark - ============== 创建 ============ 56 #pragma mark SQLite 约束 57 58 /** 59 * NOT NULL 约束:确保某列不能有 NULL 值。 60 * DEFAULT 约束:当某列没有指定值时,为该列提供默认值。 61 * UNIQUE 约束:确保某列中的所有值是不同的。 62 * PRIMARY Key 约束:唯一标识数据库表中的各行/记录。 63 * CHECK 约束:CHECK 约束确保某列中的所有值满足一定条件。 64 * AUTOINCREMENT : 自动递增 65 */ 66 - (void)createConstraintTable{ 67 68 //COMPANY 表 69 if (![_db tableExists:TABLE_NAME_COMPANY]) { 70 71 NSDictionary *table = @{@"ID" : @"INTEGER PRIMARY KEY AUTOINCREMENT", 72 @"NAME" : @"TEXT NOT NULL", 73 @"AGE" : @"INT NOT NULL", 74 @"ADDRESS" : @"CHAR(50) DEFAULT 'shenzhen'", 75 @"SALARY" : @"REAL CHECK(SALARY >0)" 76 }; 77 [_db createTable:TABLE_NAME_COMPANY columns:table]; 78 } 79 80 //DEPARTMENT 表 81 if (![_db tableExists:TABLE_NAME_DEPARTMENT]) { 82 NSDictionary *table = @{@"ID" : @"INT PRIMARY KEY NOT NULL", 83 @"DEPT" : @"CHAR(50) NOT NULL", 84 @"EMP_ID" : @"INT NOT NULL" 85 }; 86 [_db createTable:TABLE_NAME_DEPARTMENT columns:table]; 87 } 88 89 //ADULT 表 90 if (![_db tableExists:TABLE_NAME_ADULT]) { 91 NSDictionary *table = @{@"EMP_ID" : @"INT NOT NULL", 92 @"ENTRYDATE" : @"TEXT NOT NULL" 93 }; 94 [_db createTable:TABLE_NAME_ADULT columns:table]; 95 //触发器 96 [self createTrigger]; 97 } 98 99 //COMPANY_BKP 表 100 if (![_db tableExists:TABLE_NAME_COMPANY_BKP]) { 101 NSDictionary *table = @{@"ID" : @"INTEGER PRIMARY KEY AUTOINCREMENT", 102 @"NAME" : @"TEXT NOT NULL", 103 @"AGE" : @"INT NOT NULL", 104 @"ADDRESS" : @"CHAR(50) DEFAULT 'shenzhen'", 105 @"SALARY" : @"REAL CHECK(SALARY >0)" 106 }; 107 [_db createTable:TABLE_NAME_COMPANY_BKP columns:table]; 108 } 109 } 110 111 112 #pragma mark - =============== 其他 ============ 113 #pragma mark 触发器 114 115 /** 116 * 触发器是数据库的回调函数,它会自动执行/指定的数据库事件发生时调用, 117 * 比如用在需要对某些表的操作之后对相应的表增删改数据 118 */ 119 - (void)createTrigger{ 120 121 #if 0 122 //删除触发器 123 NSString *dropSql = @"DROP TRIGGER audit_log"; 124 [_db executeUpdate:dropSql]; 125 #endif 126 NSString *sql = [NSString stringWithFormat:@"CREATE TRIGGER audit_log AFTER INSERT ON COMPANY BEGIN INSERT INTO ADULT(EMP_ID, ENTRYDATE) VALUES (new.ID, datetime('now')); END"]; 127 [_db executeUpdate:sql]; 128 129 //插入数据 130 // [self performSelector:@selector(insertData) withObject:nil afterDelay:1.0f]; 131 } 132 133 #pragma mark 索引、视图、事务 134 /** 135 * 客户端应该不需要考虑使用索引、视图 136 * 索引:索引只有在数据量大的表中才会使用 137 * 视图: 视图(View)是一种虚表 138 * 事务:处理大量数据的时候使用 139 [[DbManager getInstance].dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) { 140 http://www.runoob.com/sqlite/sqlite-transaction.html 141 }] 142 */ 143 144 145 #pragma mark ALTER 146 147 /** 148 * 重命名表,还可以在已有的表中添加额外的列 149 */ 150 - (void)renameTableOrInsertColumn{ 151 #if 1 152 //重命名表 153 NSString *sql = @"ALTER TABLE COMPANY RENAME TO OLD_COMPANY"; 154 #else 155 //添加列 156 NSString *sql = @"ALTER TABLE COMPANY ADD COLUMN SEX char(1)"; 157 #endif 158 [_db executeUpdate:sql]; 159 } 160 161 162 #pragma mark - ============= 查询 ============= 163 164 #pragma mark SQLite Joins 165 /** 166 * 交叉连接 - CROSS JOIN 167 * 内连接 - INNER JOIN 168 * 外连接 - OUTER JOIN 169 */ 170 - (void)searchJoins{ 171 #if 0 172 //CROSS JOIN 173 NSString *sql = [NSString stringWithFormat:@"SELETE EMP_ID,NAME,DEPT FROM COMPANY CROSS JOIN DEPARTMENT"]; 174 175 #elif 1 176 //INNER JOIN 177 NSString *sql = [NSString stringWithFormat:@"SELECT EMP_ID, NAME, DEPT FROM COMPANY INNER JOIN DEPARTMENT ON COMPANY.ID = DEPARTMENT.EMP_ID"]; 178 #else 179 //OUTER JOIN 180 NSString *sql = [NSString stringWithFormat:@"SELECT EMP_ID, NAME, DEPT FROM COMPANY LEFT OUTER JOIN DEPARTMENT ON COMPANY.ID = DEPARTMENT.EMP_ID"]; 181 #endif 182 FMResultSet *resultSet = [_db executeQuery:sql]; 183 while ([resultSet next]) { 184 NSLog(@"EMP_ID = %d",[resultSet intForColumn:@"EMP_ID"]); 185 NSLog(@"NAME = %@", [resultSet stringForColumn:@"NAME"]); 186 NSLog(@"DEPT = %@", [resultSet stringForColumn:@"DEPT"]); 187 } 188 [resultSet close]; 189 } 190 191 #pragma mark SQLite UNIONS子句 192 /** 193 * SQLite的 UNION 子句/运算符用于合并两个或多个 SELECT 语句的结果 194 */ 195 - (void)searchUnion{ 196 #if 0 197 //UNION 重复行不返回 198 NSString *sql = [NSString stringWithFormat:@"SELECT EMP_ID, NAME, DEPT FROM COMPANY INNER JOIN DEPARTMENT ON COMPANY.ID = DEPARTMENT.EMP_ID UNION SELECT EMP_ID, NAME, DEPT FROM COMPANY LEFT OUTER JOIN DEPARTMENT ON COMPANY.ID = DEPARTMENT.EMP_ID"]; 199 #else 200 //UNION ALL 把重复行也返回 201 NSString *sql = [NSString stringWithFormat:@"SELECT EMP_ID, NAME, DEPT FROM COMPANY INNER JOIN DEPARTMENT ON COMPANY.ID = DEPARTMENT.EMP_ID UNION ALL SELECT EMP_ID, NAME, DEPT FROM COMPANY LEFT OUTER JOIN DEPARTMENT ON COMPANY.ID = DEPARTMENT.EMP_ID"]; 202 #endif 203 FMResultSet *resultSet = [_db executeQuery:sql]; 204 while ([resultSet next]) { 205 NSLog(@"EMP_ID = %d",[resultSet intForColumn:@"EMP_ID"]); 206 NSLog(@"NAME = %@", [resultSet stringForColumn:@"NAME"]); 207 NSLog(@"DEPT = %@", [resultSet stringForColumn:@"DEPT"]); 208 } 209 [resultSet close]; 210 } 211 #pragma mark 子查询 212 /** 213 * 子查询可以与 SELECT、INSERT、UPDATE 和 DELETE 语句一起使用,可伴随着使用运算符如 =、<、>、>=、<=、IN、BETWEEN 等。 214 * 子查询必须用括号括起来。 215 * 子查询在 SELECT 子句中只能有一个列,除非在主查询中有多列,与子查询的所选列进行比较。 216 * ORDER BY 不能用在子查询中,虽然主查询可以使用 ORDER BY。可以在子查询中使用 GROUP BY,功能与 ORDER BY 相同。 217 * 子查询返回多于一行,只能与多值运算符一起使用,如 IN 运算符。 218 * BETWEEN 运算符不能与子查询一起使用,但是,BETWEEN 可在子查询内使用。 219 */ 220 221 - (void)subquery{ 222 #if 0 223 NSString *selectSQL = @"SELECT *FROM COMPANY WHERE ID IN (SELECT ID FROM COMPANY WHERE SALARY > 45000)"; 224 FMResultSet *resultSet = [_db executeQuery:selectSQL]; 225 while ([resultSet next]) { 226 NSLog(@"EMP_ID = %d",[resultSet intForColumn:@"EMP_ID"]); 227 NSLog(@"NAME = %@", [resultSet stringForColumn:@"NAME"]); 228 NSLog(@"DEPT = %@", [resultSet stringForColumn:@"DEPT"]); 229 } 230 [resultSet close]; 231 #elif 1 232 NSString *insertSQL = @"INSERT INTO COMPANY_BKP SELECT * FROM COMPANY WHERE ID IN (SELECT ID FROM COMPANY)";//整个 COMPANY 表复制到 COMPANY_BKP 233 [_db executeUpdate:insertSQL]; 234 #elif 0 235 NSString *updateSQL = @"UPDATE COMPANY SET SALARY = SALARY * 0.50 WHERE AGE IN (SELECT AGE FROM COMPANY_BKP WHERE AGE >= 27 )"; 236 [_db executeUpdate:updateSQL]; 237 #else 238 NSString *deleteSQL = @"DELETE FROM COMPANY WHERE AGE IN (SELECT AGE FROM COMPANY_BKP WHERE AGE > 27 )"; 239 [_db executeUpdate:deleteSQL]; 240 241 #endif 242 } 243 244 245 #pragma mark SQLite函数 246 - (void)sqlFunction{ 247 #if 0 248 //计算一个数据库表中的行数 249 NSString *countSQL = @"SELECT count(*) FROM COMPANY"; 250 251 //某列的最大值 252 NSString *maxSQL = @"SELECT max(salary) FROM COMPANY"; 253 254 //某列的最小值 255 NSString *minSQL = @"SELECT min(salary) FROM COMPANY"; 256 257 //某列的平均值 258 NSString *avgSQL = @"SELECT avg(salary) FROM COMPANY"; 259 260 //数值列计算总和 261 NSString *sumSQL = @"SELECT sum(salary) FROM COMPANY"; 262 263 //返回随机数 264 NSString *randomSQL = @"SELECT random() AS Random"; 265 266 //参数绝对值 267 NSString *absSQL = @"SELECT abs(5), abs(-15), abs(NULL), abs(0), abs(\"ABC\")";//大写字母用双引号 268 269 //字符串转换为小写字母 270 NSString *lowerSQL = @"SELECT lower(name) FROM COMPANY"; 271 272 //返回字符串长度 273 NSString *lengthSQL = @"SELECT name, length(name) FROM COMPANY"; 274 #endif 275 276 277 } 278 279 #pragma mark SQLite LIKE 关键字 280 /** 281 * SQLite 的 LIKE 运算符是用来匹配通配符指定模式的文本值。如果搜索表达式与模式表达式匹配,LIKE 运算符将返回真(true),也就是 1。这里有两个通配符与 LIKE 运算符一起使用: 282 * 百分号 (%) 283 * 下划线 (_) 284 */ 285 - (void)searchLike_SQL{ 286 #if 0 287 NSString *sql1 = @"SELECT * FROM COMPANY WHERE SALARY LIKE '200%'";//查找以 200 开头的任意值 288 NSString *sql2 = @"SELECT * FROM COMPANY WHERE AGE LIKE '%200%'";//任意位置包含 200 的任意值 289 NSString *sql3 = @"SELECT * FROM COMPANY WHERE SALARY LIKE '_00%'";//第二位和第三位为 00 的任意值 290 NSString *sql4 = @"SELECT * FROM COMPANY WHERE SALARY LIKE '2_%_%'";//查找以 2 开头,且长度至少为 3 个字符的任意值 291 NSString *sql5 = @"SELECT * FROM COMPANY WHERE SALARY LIKE '%2'";//以 2 结尾的任意值 292 NSString *sql6 = @"SELECT * FROM COMPANY WHERE SALARY LIKE '_2%3'"; //查找第二位为 2,且以 3 结尾的任意值 293 NSString *sql7 = @"SELECT * FROM COMPANY WHERE SALARY LIKE '2___3'";//找长度为 5 位数,且以 2 开头以 3 结尾的任意值 294 #endif 295 } 296 297 /** 298 * GLOB 运算符是用来匹配通配符指定模式的文本值 299 * 与 LIKE 运算符不同的是,GLOB 是大小写敏感的 300 * 星号(*)代表零个、一个或多个数字或字符。问号(?)代表一个单一的数字或字符。这些符号可以被组合使用。 301 */ 302 #pragma SQLite GLOB 子句 303 - (void)searchGLOB_SQL{ 304 #if 0 305 NSString *sql1 = @"SELECT * FROM COMPANY WHERE SALARY GLOB '200*'";//查找以 200 开头的任意值 306 NSString *sql2 = @"SELECT * FROM COMPANY WHERE SALARY GLOB '*200*'";//查找任意位置包含 200 的任意值 307 NSString *sql3 = @"SELECT * FROM COMPANY WHERE SALARY GLOB '?00*'";//查找第二位和第三位为 00 的任意值 308 NSString *sql4 = @"SELECT * FROM COMPANY WHERE SALARY GLOB '2??'";//查找以 2 开头,且长度至少为 3 个字符的任意值 309 NSString *sql5 = @"SELECT * FROM COMPANY WHERE SALARY GLOB '*2'";//查找以 2 结尾的任意值 310 NSString *sql6 = @"SELECT * FROM COMPANY WHERE SALARY GLOB '?2*3'";//查找第二位为 2,且以 3 结尾的任意值 311 NSString *sql7 = @"SELECT * FROM COMPANY WHERE SALARY GLOB '2???3'";//查找长度为 5 位数,且以 2 开头以 3 结尾的任意值 312 #endif 313 } 314 315 #pragma mark SQLite LIMIT 字句 316 /** 317 * LIMIT 子句用于限制由 SELECT 语句返回的数据数量 318 */ 319 - (void)searchLIMIT_SQL{ 320 #if 0 321 NSString *sql1 = @"SELECT * FROM COMPANY WHERE SALARY LIMIT 6"; 322 NSString *sql2 = @"SELECT * FROM COMPANY WHERE SALARY LIMIT 3 OFFSET 2";//从第二个开始取,取3个 323 #endif 324 } 325 326 #pragma mark SQLite Order By 327 328 /** 329 * ORDER BY 子句是用来基于一个或多个列按升序或降序顺序排列数据 330 */ 331 - (void)searchORDER_BY_SQL{ 332 #if 0 333 //升序 334 NSString *sql = @"SELECT * FROM COMPANY ORDER BY SALARY ASC"; 335 NSString *descSql = @"SELECT * FROM COMPANY ORDER BY SALARY DESC"; 336 #endif 337 338 } 339 340 #pragma mark SQLite group by 341 /** 342 * GROUP BY 子句用于与 SELECT 语句一起使用,来对相同的数据进行分组。 343 * GROUP BY 子句放在 WHERE 子句之后,放在 ORDER BY 子句之前 344 */ 345 - (void)searchGROUP_BY_SQL{ 346 #if 0 347 NSString *sql = @"SELECT NAME, SUM(SALARY) FROM COMPANY GROUP BY NAME ORDER BY NAME DESC"; 348 #endif 349 } 350 351 #pragma mark SQLite having 352 353 /** 354 * HAVING 子句允许指定条件来过滤将出现在最终结果中的分组结果 355 */ 356 - (void)searchHAVING_SQL{ 357 #if 0 358 NSString *sql = @"SELECT * FROM COMPANY GROUP BY name HAVING count(name) > 2"; 359 #endif 360 } 361 362 #pragma mark SQLite Having 363 364 /** 365 * 消除所有重复的记录,并只获取唯一一次记录 366 */ 367 - (void)searchDISTINCT_SQL{ 368 #if 0 369 NSString *sql = @"SELECT DISTINCT name FROM COMPANY"; 370 #endif 371 } 372 373 #pragma mark SQLite AND/OR 374 375 - (void)searchAN_OR_SQL{ 376 #if 0 377 NSString *orSql = @"SELECT * FROM COMPANY WHERE AGE >= 25 OR SALARY >= 65000"; 378 NSString *andSql = @"SELECT * FROM COMPANY WHERE AGE >= 25 and SALARY >= 65000"; 379 #endif 380 } 381 @end