search
HomeDatabaseMysql TutorialWhat are the constraints of MySQL?

What are the constraints of MySQL?

May 26, 2023 pm 07:14 PM
mysql

    1. Overview

    Concept: Constraints are rules that act on fields in a table and are used to limit storage in the table The data.

    Purpose: Ensure the accuracy, validity and integrity of the data in the database.

    Category:

    What are the constraints of MySQL?

    Note: Constraints act on the fields in the table and can be created Table/Add constraints when modifying the table.

    2. Constraint demonstration

    We have introduced the common constraints in the database and the keywords involved in the constraints. So how do we specify these constraints when creating and modifying tables? Well, next we will demonstrate it through a case.

    Case requirements: Complete the creation of the table structure according to the requirements. The requirements are as follows:

    What are the constraints of MySQL?

    ##The corresponding table creation statement is:

    CREATE TABLE tb_user (
    	id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'ID唯一标识',
    	NAME VARCHAR ( 10 ) NOT NULL UNIQUE COMMENT '姓名',
    	age INT CHECK ( age > 0 && age <= 120 ) COMMENT &#39;年龄&#39;,
    	STATUS CHAR ( 1 ) DEFAULT &#39;1&#39; COMMENT &#39;状态&#39;,
    	gender CHAR ( 1 ) COMMENT &#39;性别&#39; 
    );

    When adding constraints to fields, we only Just add the constraint keyword after the field, and you need to pay attention to its syntax.

    We execute the above SQL to create the table structure, and then we can test it through a set of data to verify whether the constraints can take effect.

    (1) First, three pieces of data were added.

    insert into tb_user(name,age,status,gender) values (&#39;Tom1&#39;,19,&#39;1&#39;,&#39;男&#39;),(&#39;Tom2&#39;,25,&#39;0&#39;,&#39;男&#39;); 
    insert into tb_user(name,age,status,gender) values (&#39;Tom3&#39;,19,&#39;1&#39;,&#39;男&#39;);

    It took 21 seconds to add three pieces of data. What is going on?

    What are the constraints of MySQL?

    Originally I thought that adding these constraints caused the slowness of new data. In fact, it is not because this is Alibaba's Linux server, and then I use the client in Linux to It only takes 0.01 seconds to connect to mysql and execute new addition, which shows that this is the time it takes for navicat to connect to the remote host.

    Even if these new constraints are added, it will cause the new data to be slow, which can only be clearly noticed in batches. It is basically invisible to a single piece of data.

    (2) Test name NOT NULL

    insert into tb_user(name,age,status,gender) values (null,19,&#39;1&#39;,&#39;男&#39;);

    What are the constraints of MySQL?

    ##(3) Test name UNIQUE (only)

    The data added above already contains Tom3. If you add it again, an error will be reported directly.

    insert into tb_user(name,age,status,gender) values (&#39;Tom3&#39;,19,&#39;1&#39;,&#39;男&#39;);

    What are the constraints of MySQL?Although an error is reported, we will find a phenomenon when we add a piece of data at this time.

    insert into tb_user(name,age,status,gender) values (&#39;Tom4&#39;,80,&#39;1&#39;,&#39;男&#39;);

    It is obviously a self-increasing ID, but there is no 4. The reason is that UNIQUE is ready to be put into the database after applying for the self-increasing ID. Then at this time, we will first check if there is one in the database. There is a value with the same name. If there is, the new addition fails. Although the new addition fails, the auto-increment ID has been applied for!

    On the contrary, when we just tested the null name, he did not apply for an id, because he had already judged that it was empty at the beginning and had not yet reached the step of applying for an id.

    Determine whether it is empty -》 Apply for auto-increment id -》 Determine whether there is already an existing value

    What are the constraints of MySQL?Summary: When new When the added name is not empty, but is the same as the previously existing data, the new addition will fail at this time, but it will apply for the primary key id.

    (4) Test CHECK

    We set the age must be greater than 0 and less than or equal to 120, otherwise the save will fail!

    age int check (age > 0 && age <= 120) COMMENT &#39;年龄&#39; ,
    insert into tb_user(name,age,status,gender) values (&#39;Tom5&#39;,-1,&#39;1&#39;,&#39;男&#39;); 
    insert into tb_user(name,age,status,gender) values (&#39;Tom5&#39;,121,&#39;1&#39;,&#39;男&#39;);

    (5) Test DEFAULT ‘1’ Default value

    STATUS CHAR ( 1 ) DEFAULT &#39;1&#39; COMMENT &#39;状态&#39;,
    insert into tb_user(name,age,gender) values (&#39;Tom5&#39;,120,&#39;男&#39;);

    (6) Above, we complete the constraints by writing SQL statements specified, what if we are Navicat client?

    Primary key auto-increment

    What are the constraints of MySQL?name unique constraint

    What are the constraints of MySQL?status defaults to 1

    What are the constraints of MySQL?3. Foreign key constraints

    1. What are foreign key constraints

    Foreign keys:

    are used to A connection is established between the data in the two tables to ensure data consistency and integrity.

    Let’s take a look at an example:

    What are the constraints of MySQL?#The emp table on the left is the employee table, which stores basic information about employees, including The employee's ID, name, age, position, salary, joining date, supervisor ID, and department ID. Stored in the employee's information is the department's ID dept_id, and the department's ID is the primary key id of the associated department table dept. The dept_id of the emp table is the foreign key, and it is associated with the primary key of another table.

    2、 不使用外键有什么影响

    通过上面的示例,我们分别来演示 添加外键 和不添加外键的区别,首先来看不添加 外键 对数据有什么影响:

    准备数据:

    CREATE TABLE dept ( id INT auto_increment COMMENT &#39;ID&#39; PRIMARY KEY, NAME VARCHAR ( 50 ) NOT NULL COMMENT &#39;部门名称&#39; ) COMMENT &#39;部门表&#39;;
    
    INSERT INTO dept (id, name) VALUES (1, &#39;研发部&#39;), (2, &#39;市场部&#39;),(3, &#39;财务部&#39;), (4, &#39;销售部&#39;), (5, &#39;总经办&#39;);
    
    CREATE TABLE emp (
    	id INT auto_increment COMMENT &#39;ID&#39; PRIMARY KEY,
    	NAME VARCHAR ( 50 ) NOT NULL COMMENT &#39;姓名&#39;,
    	age INT COMMENT &#39;年龄&#39;,
    	job VARCHAR ( 20 ) COMMENT &#39;职位&#39;,
    	salary INT COMMENT &#39;薪资&#39;,
    	entrydate date COMMENT &#39;入职时间&#39;,
    	managerid INT COMMENT &#39;直属领导ID&#39;,
    dept_id INT COMMENT &#39;部门ID&#39; 
    ) COMMENT &#39;员工表&#39;;
    
    INSERT INTO emp (id, name, age, job,salary, entrydate, managerid, dept_id) VALUES 
    (1, &#39;金庸&#39;, 66, &#39;总裁&#39;,20000, &#39;2000-01-01&#39;, null,5),
    (2, &#39;张无忌&#39;, 20, &#39;项目经理&#39;,12500, &#39;2005-12-05&#39;, 1,1), 
    (3, &#39;杨逍&#39;, 33, &#39;开发&#39;, 8400,&#39;2000-11-03&#39;, 2,1),
    (4, &#39;韦一笑&#39;, 48, &#39;开 发&#39;,11000, &#39;2002-02-05&#39;, 2,1), 
    (5, &#39;常遇春&#39;, 43, &#39;开发&#39;,10500, &#39;2004-09-07&#39;, 3,1),
    (6, &#39;小昭&#39;, 19, &#39;程 序员鼓励师&#39;,6600, &#39;2004-10-12&#39;, 2,1);

    What are the constraints of MySQL?

    接下来,我们可以做一个测试,删除id为1的部门信息。

    What are the constraints of MySQL?

    结果,我们看到删除成功,而删除成功之后,部门表不存在id为1的部门,而在emp表中还有很多的员工,关联的为id为1的部门,此时就出现了数据的不完整性。 而要想解决这个问题就得通过数据库的外键约束。

    正常开发当中有时候会通过业务代码来控制数据的不完整性,例如删除部门的时候会先根据部门id去查看一下有没有对应的员工表,如果有则删除失败,没有则删除成功。

    3、 添加外键的语法

    可以在创建表的时候直接添加外键,也可以对现已存在的表添加外键。

    (1)方式一

    CREATE TABLE 表名( 
    	字段名 数据类型, 
    	... 
    	[CONSTRAINT] [外键名称] FOREIGN KEY (外键字段名) REFERENCES 主表 (主表列名) 
    );

    使用示例:

    CREATE TABLE emp (
    	id INT auto_increment COMMENT &#39;ID&#39; PRIMARY KEY,
    	NAME VARCHAR ( 50 ) NOT NULL COMMENT &#39;姓名&#39;,
    	age INT COMMENT &#39;年龄&#39;,
    	job VARCHAR ( 20 ) COMMENT &#39;职位&#39;,
    	salary INT COMMENT &#39;薪资&#39;,
    	entrydate date COMMENT &#39;入职时间&#39;,
    	managerid INT COMMENT &#39;直属领导ID&#39;,
    	dept_id INT COMMENT &#39;部门ID&#39;,
    	CONSTRAINT fk_emp_dept_id FOREIGN KEY (dept_id) REFERENCES dept (id)  
    ) COMMENT &#39;员工表&#39;;

    也可以省略掉CONSTRAINT fk_emp_dept_id 这样mysql就会自动给我们起外键名称。

    方式二:对现存在的表添加外键

    ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表 (主表列名) ;

    使用示例:

    alter table emp add constraint fk_emp_dept_id FOREIGN KEY (dept_id) REFERENCES dept(id);

    方式三:Navicat添加外键

    What are the constraints of MySQL?

    删除外键:

    ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;

    使用示例:

    alter table emp drop foreign key fk_emp_dept_id;

    4、 删除/更新行为

    我们将在父表数据删除时发生的限制行为称为删除/更新行为,此行为是在添加外键之后发生的。具体的删除/更新行为有以下几种:

    What are the constraints of MySQL?

    默认的MySQL 8.0.27版本中,RESTRICT是用于删除和更新行的行为!但是,不同的版本可能会有不同的行为

    What are the constraints of MySQL?

    具体语法为:

    ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名 (主表字段名) ON UPDATE CASCADE ON DELETE CASCADE;

    就是比原先添加外键后面多了这些ON UPDATE CASCADE ON DELETE CASCADE,代表的是更新时采用CASCADE ,删除时也采用CASCADE

    5、 演示删除/更新行为

    (1)演示RESTRICT

    在对父表中的记录进行删除或更新操作时,需要先检查该记录是否存在关联的外键,如果存在,则不允许执行删除或更新操作。 (与 NO ACTION 一致) 默认行为

    首先要添加外键,默认是RESTRICT行为!

    alter table emp add constraint fk_emp_dept_id FOREIGN KEY (dept_id) REFERENCES dept(id);

    删除父表中id为5的记录时,会因为emp表中的dept_id存在5而报错。假如要更新id也同样会报错的!

    What are the constraints of MySQL?

    (2)演示CASCADE

    当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有,则

    也删除/更新外键在子表中的记录。

    删除外键的语法:

    ALTER TABLE 表名 DROP FOREIGN KEY 外键约束名;

    删除外键的示例:

    alter table emp drop foreign key fk_emp_dept_id;

    指定外键的删除更新行为为cascade

    alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id) on update cascade on delete cascade ;

    修改父表id为1的记录,将id修改为6

    What are the constraints of MySQL?

    我们发现,原来在子表中dept_id值为1的记录,现在也变为6了,这就是cascade级联的效果。

    在一般的业务系统中,不会修改一张表的主键值。

    删除父表id为6的记录

    What are the constraints of MySQL?

    我们发现,父表的数据删除成功了,但是子表中关联的记录也被级联删除了。

    (3)演示SET NULL

    当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(这就要求该外键允许取null)。

    alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id) on update set null on delete set null ;

    在执行测试之前,我们需要先移除已创建的外键 fk_emp_dept_id。然后再通过数据脚本,将emp、dept表的数据恢复了。

    接下来,我们删除id为1的数据,看看会发生什么样的现象。

    What are the constraints of MySQL?

    我们发现父表的记录是可以正常的删除的,父表的数据删除之后,再打开子表 emp,我们发现子表emp的dept_id字段,原来dept_id为1的数据,现在都被置为NULL了。

    What are the constraints of MySQL?

    This is the effect of the delete/update behavior of SET NULL.

    4. Is it better to use auto-increment or uuid as the primary key ID?

    When designing tables in mysql, mysql officially recommends not to use uuid or discontinuous and non-repeating snowflake ids (long-shaped and (unique), but it is recommended to continuously increase the primary key id. The official recommendation is auto_increment. So why is it not recommended to use uuid? What are the disadvantages of using uuid?

    1. Test uuid, auto-increment id and random number insertion efficiency

    First create three tables, user_auto_key represents the auto-increment table, user_uuid represents It is the uuid where the id is stored, and random_key represents the table id which is the snowflake id. Then connect to jdbc to insert data in batches. The test results are as follows:

    What are the constraints of MySQL?

    When the existing data volume is 130W: Let’s test inserting 10w of data again to see what will happen. Result:

    What are the constraints of MySQL?

    It can be seen that when the data amount is about 100W, the insertion efficiency of uuid is at the bottom, and after adding 130W of data in the sequence, the time of uudi plummets again . The overall efficiency ranking of time usage is: auto_key>random_key>uuid, uuid has the lowest efficiency

    2. Disadvantages of using auto-incrementing ids

    1. Once others crawl your database, they can obtain your business growth information based on the database's auto-increment ID, and it is easy to analyze your business situation

    2. For high-concurrency loads , InnoDB will cause obvious lock contention when inserting according to the primary key. The upper bound of the primary key will become a hot spot for contention, because all insertions occur here, and concurrent insertion will lead to gap lock competition

    3. The Auto_Increment lock mechanism will cause the grabbing of auto-increment locks, which will cause a certain performance loss

    4. It is quite troublesome if auto-increment ID involves data migration!

    5. And once it comes to sub-databases and sub-tables, it is quite troublesome to automatically increase the ID!

    3. Disadvantages of using uuid

    Because uuid has no rules relative to sequential self-increasing IDs, the value of a new row is not necessarily higher than The previous primary key value was larger, so InnoDB could not always insert new rows to the end of the index. Instead, it needed to find a new suitable location for the new row to allocate new space. This process requires performing multiple additional operations, and the disorder of the data may cause data to be scattered, resulting in the following problems:

    1. The target page written is likely to have been flushed to the disk and moved up from the cache In addition, or has not been loaded into the cache, innodb has to find and read the target page from disk into memory before inserting, which will cause a lot of random IO

    2. Because the write is chaotic In order, innodb has to perform page splitting operations frequently in order to allocate space for new rows. Page splitting leads to the movement of a large amount of data. At least three pages need to be modified at one time.

    3. Due to frequent page splitting Split, the page will become sparse and filled irregularly, which will eventually lead to data fragmentation

    Page splitting and fragmentation problems, uuid will indeed cause this problem, but Snowflake can solve this problem, The snowflake algorithm is naturally sequential and the newly inserted ID must be the largest, so I think using the snowflake algorithm is a very good choice!

    5. Use foreign keys as little as possible in actual development

    Primary keys and indexes are indispensable. They can not only optimize the data retrieval speed, but also save developers from other work.

    Focus of conflict: Whether the database design requires foreign keys. There are two questions here:

    One is how to ensure the integrity and consistency of database data;

    The second is the impact of the first one on performance.

    Here are divided into two viewpoints: positive and negative, for reference!

    1. Positive point of view

    1. The database itself ensures data consistency and integrity, which is more reliable, because it is difficult for the program to guarantee 100% data integrity, and it is difficult to use external Even when the database server crashes or other problems occur, the key can ensure the consistency and integrity of the data to the greatest extent.

    2. Database design with primary and foreign keys can increase the readability of the ER diagram, which is very important in database design.

    3. The business logic explained by foreign keys to a certain extent will make the design thoughtful, specific and comprehensive.

    There is a one-to-many relationship between database and application. Application A will maintain the integrity of its part of the data. When the system becomes larger, application B is added. The two applications A and B may be developed differently. The team does it. How to coordinate to ensure data integrity, and if a new C application is added after a year, how to deal with it?

    2. Opposite view

    1. Triggers or applications can be used to ensure data integrity

    2. Overemphasis or use of primary keys/foreign keys will increase development time Difficulty, leading to problems such as too many tables

    3. Data management is simple, easy to operate, and high performance when no foreign keys are used (operations such as import and export are faster when inserting, updating, and deleting data)

    Don’t even think about foreign keys in a massive database. Just imagine, a program needs to insert millions of records every day. When there are foreign key constraints, it needs to scan whether the record is qualified every time. Usually it is more than A field has a foreign key, so the number of scans increases exponentially! One of my programs was completed in 3 hours. If foreign keys were added, it would take 28 hours!

    3. Conclusion

    1. In large systems (low performance requirements, high security requirements), use foreign keys; in large systems (high performance requirements, security requirements are controlled by yourself), No foreign keys are needed; for small systems, it is better to use foreign keys.

    2. Use foreign keys appropriately and not pursue them excessively

    In order to ensure the consistency and integrity of the data, you can control it through programs without using foreign keys. At this time, a layer should be written to implement data protection, and then various applications of the database can be accessed through this layer.

    It should be noted that:

    MySQL allows the use of foreign keys, but for integrity checking purposes, this feature is ignored in all table types except the InnoDB table type. This may seem weird, but it's actually quite normal: performing an integrity check after every insert, update, and delete of all foreign keys in a database is a time- and resource-consuming process that can impact performance, especially when processing complex of or the number of winding connections. Therefore, users can choose the one that suits their specific needs on the basis of the table.

    So, if you need better performance and don't need integrity checking, you can choose to use the MyISAM table type. If you want to build a table based on referential integrity in MySQL and want to maintain good performance on this basis. For better performance, it is best to choose the table structure as innoDB type

    The above is the detailed content of What are the constraints of MySQL?. For more information, please follow other related articles on the PHP Chinese website!

    Statement
    This article is reproduced at:亿速云. If there is any infringement, please contact admin@php.cn delete
    Explain the role of InnoDB redo logs and undo logs.Explain the role of InnoDB redo logs and undo logs.Apr 15, 2025 am 12:16 AM

    InnoDB uses redologs and undologs to ensure data consistency and reliability. 1.redologs record data page modification to ensure crash recovery and transaction persistence. 2.undologs records the original data value and supports transaction rollback and MVCC.

    What are the key metrics to look for in an EXPLAIN output (type, key, rows, Extra)?What are the key metrics to look for in an EXPLAIN output (type, key, rows, Extra)?Apr 15, 2025 am 12:15 AM

    Key metrics for EXPLAIN commands include type, key, rows, and Extra. 1) The type reflects the access type of the query. The higher the value, the higher the efficiency, such as const is better than ALL. 2) The key displays the index used, and NULL indicates no index. 3) rows estimates the number of scanned rows, affecting query performance. 4) Extra provides additional information, such as Usingfilesort prompts that it needs to be optimized.

    What is the Using temporary status in EXPLAIN and how to avoid it?What is the Using temporary status in EXPLAIN and how to avoid it?Apr 15, 2025 am 12:14 AM

    Usingtemporary indicates that the need to create temporary tables in MySQL queries, which are commonly found in ORDERBY using DISTINCT, GROUPBY, or non-indexed columns. You can avoid the occurrence of indexes and rewrite queries and improve query performance. Specifically, when Usingtemporary appears in EXPLAIN output, it means that MySQL needs to create temporary tables to handle queries. This usually occurs when: 1) deduplication or grouping when using DISTINCT or GROUPBY; 2) sort when ORDERBY contains non-index columns; 3) use complex subquery or join operations. Optimization methods include: 1) ORDERBY and GROUPB

    Describe the different SQL transaction isolation levels (Read Uncommitted, Read Committed, Repeatable Read, Serializable) and their implications in MySQL/InnoDB.Describe the different SQL transaction isolation levels (Read Uncommitted, Read Committed, Repeatable Read, Serializable) and their implications in MySQL/InnoDB.Apr 15, 2025 am 12:11 AM

    MySQL/InnoDB supports four transaction isolation levels: ReadUncommitted, ReadCommitted, RepeatableRead and Serializable. 1.ReadUncommitted allows reading of uncommitted data, which may cause dirty reading. 2. ReadCommitted avoids dirty reading, but non-repeatable reading may occur. 3.RepeatableRead is the default level, avoiding dirty reading and non-repeatable reading, but phantom reading may occur. 4. Serializable avoids all concurrency problems but reduces concurrency. Choosing the appropriate isolation level requires balancing data consistency and performance requirements.

    MySQL vs. Other Databases: Comparing the OptionsMySQL vs. Other Databases: Comparing the OptionsApr 15, 2025 am 12:08 AM

    MySQL is suitable for web applications and content management systems and is popular for its open source, high performance and ease of use. 1) Compared with PostgreSQL, MySQL performs better in simple queries and high concurrent read operations. 2) Compared with Oracle, MySQL is more popular among small and medium-sized enterprises because of its open source and low cost. 3) Compared with Microsoft SQL Server, MySQL is more suitable for cross-platform applications. 4) Unlike MongoDB, MySQL is more suitable for structured data and transaction processing.

    How does MySQL index cardinality affect query performance?How does MySQL index cardinality affect query performance?Apr 14, 2025 am 12:18 AM

    MySQL index cardinality has a significant impact on query performance: 1. High cardinality index can more effectively narrow the data range and improve query efficiency; 2. Low cardinality index may lead to full table scanning and reduce query performance; 3. In joint index, high cardinality sequences should be placed in front to optimize query.

    MySQL: Resources and Tutorials for New UsersMySQL: Resources and Tutorials for New UsersApr 14, 2025 am 12:16 AM

    The MySQL learning path includes basic knowledge, core concepts, usage examples, and optimization techniques. 1) Understand basic concepts such as tables, rows, columns, and SQL queries. 2) Learn the definition, working principles and advantages of MySQL. 3) Master basic CRUD operations and advanced usage, such as indexes and stored procedures. 4) Familiar with common error debugging and performance optimization suggestions, such as rational use of indexes and optimization queries. Through these steps, you will have a full grasp of the use and optimization of MySQL.

    Real-World MySQL: Examples and Use CasesReal-World MySQL: Examples and Use CasesApr 14, 2025 am 12:15 AM

    MySQL's real-world applications include basic database design and complex query optimization. 1) Basic usage: used to store and manage user data, such as inserting, querying, updating and deleting user information. 2) Advanced usage: Handle complex business logic, such as order and inventory management of e-commerce platforms. 3) Performance optimization: Improve performance by rationally using indexes, partition tables and query caches.

    See all articles

    Hot AI Tools

    Undresser.AI Undress

    Undresser.AI Undress

    AI-powered app for creating realistic nude photos

    AI Clothes Remover

    AI Clothes Remover

    Online AI tool for removing clothes from photos.

    Undress AI Tool

    Undress AI Tool

    Undress images for free

    Clothoff.io

    Clothoff.io

    AI clothes remover

    AI Hentai Generator

    AI Hentai Generator

    Generate AI Hentai for free.

    Hot Article

    R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
    4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. Best Graphic Settings
    4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. How to Fix Audio if You Can't Hear Anyone
    4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
    WWE 2K25: How To Unlock Everything In MyRise
    1 months agoBy尊渡假赌尊渡假赌尊渡假赌

    Hot Tools

    SublimeText3 Mac version

    SublimeText3 Mac version

    God-level code editing software (SublimeText3)

    PhpStorm Mac version

    PhpStorm Mac version

    The latest (2018.2.1) professional PHP integrated development tool

    WebStorm Mac version

    WebStorm Mac version

    Useful JavaScript development tools

    Atom editor mac version download

    Atom editor mac version download

    The most popular open source editor

    Dreamweaver Mac version

    Dreamweaver Mac version

    Visual web development tools