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

 

 

posted on 2016-09-05 22:08  hansbsf  阅读(371)  评论(0编辑  收藏  举报

导航