python创建数据库表,python对数据库表格里面的内容增删查改怎么写
大家好,今天来为大家解答python创建数据库表这个问题的一些问题点,包括python对数据库表格里面的内容增删查改怎么写也一样很多人还不知道,因此呢,今天就来为大家分析分析,现在让我们一起来看看吧!如果解决了您的问题,还望您关注下本站哦,谢谢~
如何用 Python 实现一个图数据库(Graph Database)
本文章是重写 500 Lines or Less系列的其中一篇,目标是重写 500 Lines or Less系列的原有项目:Dagoba: an in-memory graph database。
Dagoba是作者设计用来展示如何从零开始自己实现一个图数据库( Graph Database)。该名字似乎来源于作者喜欢的一个乐队,另一个原因是它的前缀 DAG也正好是有向无环图( Directed Acyclic Graph)的缩写。本文也沿用了该名称。
图是一种常见的数据结构,它将信息描述为若干独立的节点( vertex,为了和下文的边更加对称,本文中称为 node),以及把节点关联起来的边( edge)。我们熟悉的链表以及多种树结构可以看作是符合特定规则的图。图在路径选择、推荐算法以及神经网络等方面都是重要的核心数据结构。
既然图的用途如此广泛,一个重要的问题就是如何存储它。如果在传统的关系数据库中存储图,很自然的做法就是为节点和边各自创建一张表,并用外键把它们关联起来。这样的话,要查找某人所有的子女,就可以写下类似下面的查询:
还好,不算太复杂。但是如果要查找孙辈呢?那恐怕就要使用子查询或者 CTE(Common Table Expression)等特殊构造了。再往下想,曾孙辈又该怎么查询?孙媳妇呢?
这样我们会意识到,SQL作为查询语言,它只是对二维数据表这种结构而设计的,用它去查询图的话非常笨拙,很快会变得极其复杂,也难以扩展。针对图而言,我们希望有一种更为自然和直观的查询语法,类似这样:
为了高效地存储和查询图这种数据结构,图数据库( Graph Database)应运而生。因为和传统的关系型数据库存在极大的差异,所以它属于新型数据库也就是 NoSql的一个分支(其他分支包括文档数据库、列数据库等)。图数据库的主要代表包括 Neo4J等。本文介绍的 Dagoba则是具备图数据库核心功能、主要用于教学和演示的一个简单的图数据库。
原文代码是使用 JavaScript编写的,在定义调用接口时大量使用了原型( prototype)这种特有的语言构造。对于其他主流语言的用户来说,原型的用法多少显得有些别扭和不自然。
考虑到本系列其他数据库示例大多是用 Python实现的,本文也按照传统,用 Python重写了原文的代码。同样延续之前的惯例,为了让读者更好地理解程序是如何逐步完善的,我们用迭代式的方法完成程序的各个组成部分。
原文在 500lines系列的 Github仓库中只包含了实现代码,并未包含测试。按照代码注释说明,测试程序位于作者的另一个代码库中,不过和 500lines版本的实现似乎略有不同。
本文实现的代码参考了原作者的测试内容,但跳过了北欧神话这个例子——我承认确实不熟悉这些神祇之间的亲缘关系,相信中文背景的读者们多数也未必了解,虽然作者很喜欢这个例子,想了想还是不要徒增困惑吧。因此本文在编写测试用例时只参考了原文关于家族亲属的例子,放弃了神话相关的部分,尽管会减少一些趣味性,相信对于入门级的代码来说这样也够用了。
本文实现程序位于代码库的 dagoba目录下。按照本系列程序的同意规则,要想直接执行各个已完成的步骤,读者可以在根目录下的 main.py找到相应的代码位置,取消注释并运行即可。
本程序的所有步骤只需要 Python3,测试则使用内置的 unittest,不需要额外的第三方库。原则上 Python3.6以上版本应该都可运行,但我只在 Python3.8.3环境下完整测试过。
本文实现的程序从最简单的案例开始,通过每个步骤逐步扩展,最终形成一个完整的程序。这些步骤包括:
接下来依次介绍各个步骤。
回想一下,图数据库就是一些点( node)和边( edge)的集合。现在我们要做出的一个重大决策是如何对节点/边进行建模。对于边来说,必须指定它的关联关系,也就是从哪个节点指向哪个节点。大多数情况下边是有方向的——父子关系不指明方向可是要乱套的!
考虑到扩展性及通用性问题,我们可以把数据保存为字典( dict),这样可以方便地添加用户需要的任何数据。某些数据是为数据库内部管理而保留的,为了明确区分,可以这样约定:以下划线开头的特殊字段由数据库内部维护,类似于私有成员,用户不应该自己去修改它们。这也是 Python社区普遍遵循的约定。
此外,节点和边存在互相引用的关系。目前我们知道边会引用到两端的节点,后面还会看到,为了提高效率,节点也会引用到边。如果仅仅在内存中维护它们的关系,那么使用指针访问是很直观的,但数据库必须考虑到序列化到磁盘的问题,这时指针就不再好用了。
为此,最好按照数据库的一般要求,为每个节点维护一个主键( _id),用主键来描述它们之间的关联关系。
我们第一步要把数据库的模型建立起来。为了测试目的,我们使用一个最简单的数据库模型,它只包含两个节点和一条边,如下所示:
按照 TDD的原则,首先编写测试:
与原文一样,我们把数据库管理接口命名为 Dagoba。目前,能够想到的最简单的测试是确认节点和边是否已经添加到数据库中:
assert_item是一个辅助方法,用于检查字典是否包含预期的字段。相信大家都能想到该如何实现,这里就不再列出了,读者可参考 Github上的完整源码。
现在,测试是失败的。用最简单的办法实现数据库:
需要注意的是,不管添加节点还是查询,程序都使用了拷贝后的数据副本,而不是直接使用原始数据。为什么要这样做?因为字典是可变的,用户可以在任何时候修改其中的内容,如果数据库不知道数据已经变化,就很容易发生难以追踪的一致性问题,最糟糕的情况下会使得数据内容彻底混乱。
拷贝数据可以避免上述问题,代价则是需要占用更多内存和处理时间。对于数据库来说,通常查询次数要远远多于修改,所以这个代价是可以接受的。
现在测试应该正常通过了。为了让它更加完善,我们可以再测试一些边缘情况,看看数据库能否正确处理异常数据,比如:
例如,如果用户尝试添加重复主键,我们预期应抛出 ValueError异常。因此编写测试如下:
为了满足以上测试,代码需要稍作修改。特别是按照 id查找主键是个常用操作,通过遍历的方法效率太低了,最好是能够通过主键直接访问。因此在数据库中再增加一个字典:
完整代码请参考 Github仓库。
在上个步骤,我们在初始化数据库时为节点明确指定了主键。按照数据库设计的一般原则,主键最好是不具有业务含义的代理主键( Surrogate key),用户不应该关心它具体的值是什么,因此让数据库去管理主键通常是更为合理的。当然,在部分场景下——比如导入外部数据——明确指定主键仍然是有用的。
为了同时支持这些要求,我们这样约定:字段 _id表示节点的主键,如果用户指定了该字段,则使用用户设置的值(当然,用户有责任保证它们不会重复);否则,由数据库自动为它分配一个主键。
如果主键是数据库生成的,事先无法预知它的值是什么,而边( edge)必须指定它所指向的节点,因此必须在主键生成后才能添加。由于这个原因,在动态生成主键的情况下,数据库的初始化会略微复杂一些。还是先写一个测试:
为支持此功能,我们在数据库中添加一个内部字段 _next_id用于生成主键,并让 add_node方法返回新生成的主键:
接下来,再确认一下边是否可以正常访问:
运行测试,一切正常。这个步骤很轻松地完成了,不过两个测试( DbModelTest和 PrimaryKeyTest)出现了一些重复代码,比如 get_item。我们可以把这些公用代码提取出来。由于 get_item内部调用了 TestCase.assertXXX等方法,看起来应该使用继承,但从 TestCase派生基类容易引起一些潜在的问题,所以我转而使用另一个技巧 Mixin:
实现数据库模型之后,接下来就要考虑如何查询它了。
在设计查询时要考虑几个问题。对于图的访问来说,几乎总是由某个节点(或符合条件的某一类节点)开始,从与它相邻的边跳转到其他节点,依次类推。所以链式调用对查询来说是一种很自然的风格。举例来说,要知道 Tom的孙子养了几只猫,可以使用类似这样的查询:
可以想象,以上每个方法都应该返回符合条件的节点集合。这种实现是很直观的,不过存在一个潜在的问题:很多时候用户只需要一小部分结果,如果它总是不计代价地给我们一个巨大的集合,会造成极大的浪费。比如以下查询:
为了避免不必要的浪费,我们需要另外一种机制,也就是通常所称的“懒式查询”或“延迟查询”。它的基本思想是,当我们调用查询方法时,它只是把查询条件记录下来,而并不立即返回结果,直到明确调用某些方法时才真正去查询数据库。
如果读者比较熟悉流行的 Python ORM,比如 SqlAlchemy或者 Django ORM的话,会知道它们几乎都是懒式查询的,要调用 list(result)或者 result[0:10]这样的方法才能得到具体的查询结果。
在 Dagoba中把触发查询的方法定义为 run。也就是说,以下查询执行到 run时才真正去查找数据:
和懒式查询( Lazy Query)相对应的,直接返回结果的方法一般称作主动查询( Eager Query)。主动查询和懒式查询的内在查找逻辑基本上是相同的,区别只在于触发机制不同。由于主动查询实现起来更加简单,出错也更容易排查,因此我们先从主动查询开始实现。
还是从测试开始。前面测试所用的简单数据库数据太少,难以满足查询要求,所以这一步先来创建一个更复杂的数据模型:
此关系的复杂之处之一在于反向关联:如果 A是 B的哥哥,那么 B就是 A的弟弟/妹妹,为了查询到他们彼此之间的关系,正向关联和反向关联都需要存在,因此在初始化数据库时需要定义的边数量会很多。
当然,父子之间也存在反向关联的问题,为了让问题稍微简化一些,我们目前只需要向下(子孙辈)查找,可以稍微减少一些关联数量。
因此,我们定义数据模型如下。为了减少重复工作,我们通过 _backward字段定义反向关联,而数据库内部为了查询方便,需要把它维护成两条边:
然后,测试一个最简单的查询,比如查找某人的所有孙辈:
这里 outcome/income分别表示从某个节点出发、或到达它的节点集合。在原作者的代码中把上述方法称为 out/in。当然这样看起来更加简洁,可惜的是 in在 Python中是个关键字,无法作为函数名。我也考虑过加个下划线比如 out_.in_这种形式,但看起来也有点怪异,权衡之后还是使用了稍微啰嗦一点的名称。
现在我们可以开始定义查询接口了。在前面已经说过,我们计划分别实现两种查询,包括主动查询( Eager Query)以及延迟查询( Lazy Query)。
它们的内在查询逻辑是相通的,看起来似乎可以使用继承。不过遵循 YAGNI原则,目前先不这样做,而是只定义两个新类,在满足测试的基础上不断扩展。以后我们会看到,与继承相比,把共同的逻辑放到数据库本身其实是更为合理的。
接下来实现访问节点的方法。由于 EagerQuery调用查询方法会立即返回结果,我们把结果记录在 _result内部字段中。虽然 node方法只返回单个结果,但考虑到其他查询方法几乎都是返回集合,为统一起见,让它也返回集合,这样可以避免同时支持集合与单结果的分支处理,让代码更加简洁、不容易出错。此外,如果查询对象不存在的话,我们只返回空集合,并不视为一个错误。
查询输入/输出节点的方法实现类似这样:
查找节点的核心逻辑在数据库本身定义:
以上使用了内部定义的一些辅助查询方法。用类似的逻辑再定义 income,它们的实现都很简单,读者可以直接参考源码,此处不再赘述。
在此步骤的最后,我们再实现一个优化。当多次调用查询方法后,结果可能会返回重复的数据,很多时候这是不必要的。就像关系数据库通常支持 unique/distinct一样,我们也希望 Dagoba能够过滤重复的数据。
假设我们要查询某人所有孩子的祖父,显然不管有多少孩子,他们的祖父应该是同一个人。因此编写测试如下:
现在来实现 unique。我们只要按照主键把重复数据去掉即可:
在上个步骤,初始化数据库指定了双向关联,但并未测试它们。因为我们还没有编写代码去支持它们,现在增加一个测试,它应该是失败的:
运行测试,的确失败了。我们看看要如何支持它。回想一下,当从边查找节点时,使用的是以下方法:
这里也有一个潜在的问题:调用 self.edges意味着遍历所有边,当数据库内容较多时,这是巨大的浪费。为了提高性能,我们可以把与节点相关的边记录在节点本身,这样要查找边只要看节点本身即可。在初始化时定义出入边的集合:
在添加边时,我们要同时把它们对应的关系同时更新到节点,此外还要维护反向关联。这涉及对字典内容的部分复制,先编写一个辅助方法:
然后,将添加边的实现修改如下:
这里的代码同时添加正向关联和反向关联。有的朋友可能会注意到代码略有重复,是的,但是重复仅出现在该函数内部,本着“三则重构”的原则,暂时不去提取代码。
实现之后,前面的测试就可以正常通过了。
在这个步骤中,我们来实现延迟查询( Lazy Query)。
延迟查询的要求是,当调用查询方法时并不立即执行,而是推迟到调用特定方法,比如 run时才执行整个查询,返回结果。
延迟查询的实现要比主动查询复杂一些。为了实现延迟查询,查询方法的实现不能直接返回结果,而是记录要执行的动作以及传入的参数,到调用 run时再依次执行前面记录下来的内容。
如果你去看作者的实现,会发现他是用一个数据结构记录执行操作和参数,此外还有一部分逻辑用来分派对每种结构要执行的动作。这样当然是可行的,但数据处理和分派部分的实现会比较复杂,也容易出错。
本文的实现则选择了另外一种不同的方法:使用 Python的内部函数机制,把一连串查询变换成一组函数,每个函数取上个函数的执行结果作为输入,最后一个函数的输出就是整个查询的结果。由于内部函数同时也是闭包,尽管每个查询的参数形式各不相同,但是它们都可以被闭包“捕获”而成为内部变量,所以这些内部函数可以采用统一的形式,无需再针对每种查询设计额外的数据结构,因而执行过程得到了很大程度的简化。
首先还是来编写测试。 LazyQueryTest和 EagerQueryTest测试用例几乎是完全相同的(是的,两种查询只在于内部实现机制不同,它们的调用接口几乎是完全一致的)。
因此我们可以把 EagerQueryTest的测试原样不变拷贝到 LazyQueryTest中。当然拷贝粘贴不是个好注意,对于比较冗长而固定的初始化部分,我们可以把它提取出来作为两个测试共享的公共函数。读者可参考代码中的 step04_lazy_query/tests/test_lazy_query.py部分。
程序把查询函数的串行执行称为管道( pipeline),用一个变量来记录它:
然后依次实现各个调用接口。每种接口的实现都是类似的:用内部函数执行真正的查询逻辑,再把这个函数添加到 pipeline调用链中。比如 node的实现类似下面:
其他接口的实现也与此类似。最后, run函数负责执行所有查询,返回最终结果;
完成上述实现后执行测试,确保我们的实现是正确的。
在前面我们说过,延迟查询与主动查询相比,最大的优势是对于许多查询可以按需要访问,不需要每个步骤都返回完整结果,从而提高性能,节约查询时间。比如说,对于下面的查询:
以上查询的意思是从孙辈中找到一个符合条件的节点即可。对该查询而言,主动查询会在调用 outcome('son')时就遍历所有节点,哪怕最后一步只需要第一个结果。而延迟查询为了提高效率,应在找到符合条件的结果后立即停止。
目前我们尚未实现 take方法。老规矩,先添加测试:
主动查询的 take实现比较简单,我们只要从结果中返回前 n条记录:
延迟查询的实现要复杂一些。为了避免不必要的查找,返回结果不应该是完整的列表( list),而应该是个按需返回的可迭代对象,我们用内置函数 next来依次返回前 n个结果:
写完后运行测试,确保它们是正确的。
从外部接口看,主动查询和延迟查询几乎是完全相同的,所以用单纯的数据测试很难确认后者的效率一定比前者高,用访问时间来测试也并不可靠。为了测试效率,我们引入一个节点访问次数的概念,如果延迟查询效率更高的话,那么它应该比主动查询访问节点的次数更少。
为此,编写如下测试:
我们为 Dagoba类添加一个成员来记录总的节点访问次数,以及两个辅助方法,分别用于获取和重置访问次数:
然后浏览代码,查找修改点。增加计数主要在从边查找节点的时候,因此修改部分如下:
此外还有 income/outcome方法,修改都很简单,这里就不再列出。
实现后再次运行测试。测试通过,表明延迟查询确实在效率上优于主动查询。
不像关系数据库的结构那样固定,图的形式可以千变万化,查询机制也必须足够灵活。从原理上讲,所有查询无非是从某个节点出发按照特定方向搜索,因此用 node/income/outcome这三个方法几乎可以组合出任意所需的查询。
但对于复杂查询,写出的代码有时会显得较为琐碎和冗长,对于特定领域来说,往往存在更为简洁的名称,例如:母亲的兄弟可简称为舅舅。对于这些场景,如果能够类似 DSL(领域特定语言)那样允许用户根据专业要求自行扩展,从而简化查询,方便阅读,无疑会更为友好。
如果读者去看原作者的实现,会发现他是用一种特殊语法 addAlias来定义自己想要的查询,调用方法时再进行查询以确定要执行的内容,其接口和内部实现都是相当复杂的。
而我希望有更简单的方法来实现这一点。所幸 Python是一种高度动态的语言,允许在运行时向类中增加新的成员,因此做到这一点可能比预想的还要简单。
为了验证这一点,编写测试如下:
无需 Dagoba的实现做任何改动,测试就可以通过了!其实我们要做的就是动态添加一个自定义的成员函数,按照 Python对象机制的要求,成员函数的第一个成员应该是名为 self的参数,但这里已经是在 UnitTest的内部,为了和测试类本身的 self相区分,新函数的参数增加了一个下划线。
此外,函数应返回其所属的对象,这是为了链式调用所要求的。我们看到,动态语言的灵活性使得添加新语法变得非常简单。
到此,一个初具规模的图数据库就形成了。
和原文相比,本文还缺少一些内容,比如如何将数据库序列化到磁盘。不过相信读者都看到了,我们的数据库内部结构基本上是简单的原生数据结构(列表+字典),因此序列化无论用 pickle或是 JSON之类方法都应该是相当简单的。有兴趣的读者可以自行完成它们。
我们的图数据库实现为了提高查询性能,在节点内部存储了边的指针(或者说引用)。这样做的好处是,无论数据库有多大,从一个节点到相邻节点的访问是常数时间,因此数据访问的效率非常高。
但一个潜在的问题是,如果数据库规模非常大,已经无法整个放在内存中,或者出于安全性等原因要实现分布式访问的话,那么指针就无法使用了,必须要考虑其他机制来解决这个问题。分布式数据库无论采用何种数据模型都是一个棘手的问题,在本文中我们没有涉及。有兴趣的读者也可以考虑 500lines系列中关于分布式和集群算法的其他一些文章。
本文的实现和系列中其他数据库类似,采用 Python作为实现语言,而原作者使用的是 JavaScript,这应该和作者的背景有关。我相信对于大多数开发者来说, Python的对象机制比 JavaScript基于原型的语法应该是更容易阅读和理解的。
当然,原作者的版本比本文版本在实现上其实是更为完善的,灵活性也更好。如果想要更为优雅的实现,我们可以考虑使用 Python元编程,那样会更接近于作者的实现,但也会让程序的复杂性大为增加。如果读者有兴趣,不妨对照着去读读原作者的版本。
python对数据库表格里面的内容增删查改怎么写
本文主要给大家介绍了关于python模拟sql语句对员工表格进行增删改查的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍:
具体需求:
员工信息表程序,实现增删改查操作:
可进行模糊查询,语法支持下面3种:
select name,age from staff_data where age> 22多个查询参数name,age用','分割
select* from staff_data where dept=人事
select* from staff_data where enroll_date like 2013
查到的信息,打印后,最后面还要显示查到的条数
可创建新员工纪录,以phone做唯一键,phone存在即提示,staff_id需自增,添加多个记录record1/record2中间用'/'分割
insert into staff_data values record1/record2
可删除指定员工信息纪录,输入员工id,即可删除
delete from staff_data where staff_id>=5andstaff_id<=10
可修改员工信息,语法如下:
update staff_table set dept=Market,phone=13566677787 where dept=运维多个set值用','分割
使用re模块,os模块,充分使用函数精简代码,熟练使用 str.split()来解析格式化字符串
由于,sql命令中的几个关键字符串有一定规律,只出现一次,并且有顺序!!!
按照key_lis= ['select','insert','delete','update','from','into','set','values','where','limit']的元素顺序分割sql.
分割元素作为sql_dic字典的key放进字典中.分割后的列表为b,如果len(b)>1,说明sql字符串中含有分割元素,同时b[0]对应上一个分割元素的值,b[-1]为下一次分割对象!
这样不断迭代直到把sql按出现的所有分割元素分割完毕,但注意这里每次循环都是先分割后赋值!!!当前分割元素比如'select'对应的值,需要等到下一个分割元素
比如'from'执行分割后的列表b,其中b[0]的值才会赋值给sql_dic['select'],所以最后一个分割元素的值,不能通过上述循环来完成,必须先处理可能是最后一个分割元素,再正常循环!!
在这sql语句中,有可能成为最后一个分割元素的'limit','values','where',按优先级别,先处理'limit',再处理'values'或'where'.....
处理完得到sql_dic后,就是你按不同命令执行,对数据文件的增删改查,最后返回处理结果!!
示例代码
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
# _*_coding:utf-8_*_# Author:Jaye Heimport reimport os def sql_parse(sql, key_lis):'''解析sql命令字符串,按照key_lis列表里的元素分割sql得到字典形式的命令sql_dic:param sql::param key_lis::return:''' sql_list= [] sql_dic={} for i in key_lis: b= [j.strip() for j in sql.split(i)] if len(b)> 1: if len(sql.split('limit'))> 1: sql_dic['limit']= sql.split('limit')[-1] if i=='where' or i=='values': sql_dic[i]= b[-1] if sql_list: sql_dic[sql_list[-1]]= b[0] sql_list.append(i) sql= b[-1] else: sql= b[0] if sql_dic.get('select'): if not sql_dic.get('from') and not sql_dic.get('where'): sql_dic['from']= b[-1] if sql_dic.get('select'): sql_dic['select']= sql_dic.get('select').split(',') if sql_dic.get('where'): sql_dic['where']= where_parse(sql_dic.get('where')) return sql_dic def where_parse(where):'''格式化where字符串为列表where_list,用'and','or','not'分割字符串:param where::return:''' casual_l= [where] logic_key= ['and','or','not'] for j in logic_key: for i in casual_l: if i not in logic_key: if len(i.split(j))> 1: ele= i.split(j) index= casual_l.index(i) casual_l.pop(index) casual_l.insert(index, ele[0]) casual_l.insert(index+1, j) casual_l.insert(index+2, ele[1]) casual_l= [k for k in casual_l if k] where_list= three_parse(casual_l, logic_key) return where_list def three_parse(casual_l, logic_key):'''处理临时列表casual_l中具体的条件,'staff_id>5'-->['staff_id','>','5']:param casual_l::param logic_key::return:''' where_list= [] for i in casual_l: if i not in logic_key: b= i.split('like') if len(b)> 1: b.insert(1,'like') where_list.append(b) else: key= ['<','=','>'] new_lis= [] opt='' lis= [j for j in re.split('([=<>])', i) if j] for k in lis: if k in key: opt+= k else: new_lis.append(k) new_lis.insert(1, opt) where_list.append(new_lis) else: where_list.append(i) return where_list def sql_action(sql_dic, title):'''把解析好的sql_dic分发给相应函数执行处理:param sql_dic::param title::return:''' key={'select': select,'insert': insert,'delete': delete,'update': update} res= [] for i in sql_dic: if i in key: res= key[i](sql_dic, title) return res def select(sql_dic, title):'''处理select语句命令:param sql_dic::param title::return:''' with open('staff_data','r', encoding='utf-8') as fh: filter_res= where_action(fh, sql_dic.get('where'), title) limit_res= limit_action(filter_res, sql_dic.get('limit')) search_res= search_action(limit_res, sql_dic.get('select'), title) return search_res def insert(sql_dic, title):'''处理insert语句命令:param sql_dic::param title::return:''' with open('staff_data','r+', encoding='utf-8') as f: data= f.readlines() phone_list= [i.strip().split(',')[4] for i in data] ins_count= 0 if not data: new_id= 1 else: last= data[-1] last_id= int(last.split(',')[0]) new_id= last_id+1 record= sql_dic.get('values').split('/') for i in record: if i.split(',')[3] in phone_list: print('\033[1;31m%s手机号已存在\033[0m'% i) else: new_record='%s,%s\n'%(str(new_id), i) f.write(new_record) new_id+= 1 ins_count+= 1 f.flush() return ['insert successful'], [str(ins_count)] def delete(sql_dic, title):'''处理delete语句命令:param sql_dic::param title::return:''' with open('staff_data','r', encoding='utf-8') as r_file,\ open('staff_data_bak','w', encoding='utf-8') as w_file: del_count= 0 for line in r_file: dic= dict(zip(title.split(','), line.split(','))) filter_res= logic_action(dic, sql_dic.get('where')) if not filter_res: w_file.write(line) else: del_count+= 1 w_file.flush() os.remove('staff_data') os.rename('staff_data_bak','staff_data') return ['delete successful'], [str(del_count)] def update(sql_dic, title):'''处理update语句命令:param sql_dic::param title::return:''' set_l= sql_dic.get('set').strip().split(',') set_list= [i.split('=') for i in set_l] update_count= 0 with open('staff_data','r', encoding='utf-8') as r_file,\ open('staff_data_bak','w', encoding='utf-8') as w_file: for line in r_file: dic= dict(zip(title.split(','), line.strip().split(','))) filter_res= logic_action(dic, sql_dic.get('where')) if filter_res: for i in set_list: k= i[0] v= i[-1] dic[k]= v line= [dic[i] for i in title.split(',')] update_count+= 1 line=','.join(line)+'\n' w_file.write(line) w_file.flush() os.remove('staff_data') os.rename('staff_data_bak','staff_data') return ['update successful'], [str(update_count)] def where_action(fh, where_list, title):'''具体处理where_list里的所有条件:param fh::param where_list::param title::return:''' res= [] if len(where_list)!= 0: for line in fh: dic= dict(zip(title.split(','), line.strip().split(','))) if dic['name']!='name': logic_res= logic_action(dic, where_list) if logic_res: res.append(line.strip().split(',')) else: res= [i.split(',') for i in fh.readlines()] return res pass def logic_action(dic, where_list):'''判断数据文件中每一条是否符合where_list条件:param dic::param where_list::return:''' logic= [] for exp in where_list: if type(exp) is list: exp_k, opt, exp_v= exp if exp[1]=='=': opt='==' logical_char="'%s'%s'%s'"%(dic[exp_k], opt, exp_v) if opt!='like': exp= str(eval(logical_char)) else: if exp_v in dic[exp_k]: exp='True' else: exp='False' logic.append(exp) res= eval(''.join(logic)) return res def limit_action(filter_res, limit_l):'''用列表切分处理显示符合条件的数量:param filter_res::param limit_l::return:''' if limit_l: index= int(limit_l[0]) res= filter_res[:index] else: res= filter_res return res def search_action(limit_res, select_list, title):'''处理需要查询并显示的title和相应数据:param limit_res::param select_list::param title::return:''' res= [] fields_list= title.split(',') if select_list[0]=='*': res= limit_res else: fields_list= select_list for data in limit_res: dic= dict(zip(title.split(','), data)) r_l= [] for i in fields_list: r_l.append((dic[i].strip())) res.append(r_l) return fields_list, res if __name__=='__main__': with open('staff_data','r', encoding='utf-8') as f: title= f.readline().strip() key_lis= ['select','insert','delete','update','from','into','set','values','where','limit'] while True: sql= input('请输入sql命令,退出请输入exit:').strip() sql= re.sub('','', sql) if len(sql)== 0:continue if sql=='exit':break sql_dict= sql_parse(sql, key_lis) fields_list, fields_data= sql_action(sql_dict, title) print('\033[1;33m结果如下:\033[0m') print('-'.join(fields_list)) for data in fields_data: print('-'.join(data))
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。
掌握Python 操作 MySQL 数据库
本文Python操作 MySQL数据库需要是使用到 PyMySQL驱动
Python操作 MySQL前提是要安装好 MySQL数据库并能正常连接使用,安装步骤详见下文。
注意:安装过程我们需要通过开启管理员权限来安装,否则会由于权限不足导致无法安装。
首先需要先下载 MySQL安装包,官网下载地址下载对应版本即可,或直接在网上拉取并安装:
权限设置:
初始化 MySQL:
启动 MySQL:
查看 MySQL运行状态:
Mysql安装成功后,默认的root用户密码为空,你可以使用以下命令来创建root用户的密码:
登陆:
创建数据库:
查看数据库:
PyMySQL模块使用 pip命令进行安装:
假如系统不支持 pip命令,可以使用以下方式安装:
pymysql.connect函数:连接上数据库
输出结果显示如下:表面数据库连接成功
使用 pymysql的 connect()方法连接数据库,connect参数解释如下:
conn.cursor():获取游标
如果要操作数据库,光连接数据是不够的,咱们必须拿到操作数据库的游标,才能进行后续的操作,游标的主要作用是用来接收数据库操作后的返回结果,比如读取数据、添加数据。通过获取到的数据库连接实例 conn下的 cursor()方法来创建游标,实例如下:
输出结果为:
cursor返回一个游标实例对象,其中包含了很多操作数据的方法,如执行sql语句,sql执行命令: execute()和 executemany()
execute(query,args=None):
executemany(query,args=None):
其他游标对象如下表:
完整数据库连接操作实例如下:
以上结果输出为:
创建表代码如下:
如下所示数据库表创建成功:
插入数据实现代码:
插入数据结果:
Python查询Mysql使用 fetchone()方法获取单条数据,使用fetchall()方法获取多条数据。
查询数据代码如下:
输出结果:
DB API中定义了一些数据库操作的错误及异常,下表列出了这些错误和异常:
本文给大家介绍 Python如何连接 Mysql进行数据的增删改查操作,文章通过简洁的代码方式进行示例演示,给使用 Python操作 Mysql的工程师提供支撑。
关于python创建数据库表的内容到此结束,希望对大家有所帮助。