{"id":19403970,"url":"https://github.com/44437/mysql","last_synced_at":"2026-02-07T10:30:56.222Z","repository":{"id":253694859,"uuid":"530689821","full_name":"44437/MySql","owner":"44437","description":null,"archived":false,"fork":false,"pushed_at":"2022-10-22T19:01:35.000Z","size":7,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-21T15:15:37.204Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/44437.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-30T14:17:23.000Z","updated_at":"2024-08-21T13:10:03.000Z","dependencies_parsed_at":"2024-08-18T21:24:05.613Z","dependency_job_id":"343a87a1-ff88-4008-a1ee-5babb4b033e4","html_url":"https://github.com/44437/MySql","commit_stats":null,"previous_names":["44437/mysql"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/44437/MySql","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/44437%2FMySql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/44437%2FMySql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/44437%2FMySql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/44437%2FMySql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/44437","download_url":"https://codeload.github.com/44437/MySql/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/44437%2FMySql/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29192602,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-07T07:37:03.739Z","status":"ssl_error","status_checked_at":"2026-02-07T07:37:03.029Z","response_time":63,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T11:32:10.311Z","updated_at":"2026-02-07T10:30:56.204Z","avatar_url":"https://github.com/44437.png","language":null,"readme":"# MySql@8\n\n---\nDatabase modeling stages:\n    \n    1- Analyze\n    2- Conceptual Design\n    3- Logical Design\n\n---\nSchema is actually a Database since MySql@5.0.2\n\nso:\n```mysql\nCREATE DATABASE my_database = CREATE SCHEMA my_database\n```\n---\nPrint function in MySql:\n```mysql\nSELECT 'hello world'\n\nOR\n\n\\! echo \"hello world\"\n```\n---\nCreate a new mysql user:\n```mysql\nCREATE USER IF NOT EXISTS ercan@localhost IDENTIFIED BY \"password123\";\nGRANT ALL PRIVILEGES ON * . * TO ercan@localhost;\nGRANT CREATE, DROP, SELECT ON * . * TO ercan@localhost;\n# ALL PRIVILEGES, CREATE, DROP, DELETE, INSERT, SELECT, UPDATE\nFLUSH PRIVILEGES; # Immediately reflect added privileges.\nSHOW GRANTS for ercan@localhost; # or '... for CURRENT_USER();'\n\nGRANT ALL ON db_name.* TO ercan@localhost;  \nGRANT ALL ON db_name.table_name TO ercan@localhost;  \nGRANT EXECUTE ON db_name.procedure_name TO ercan@localhost;  \n\n# Column specific:\nGRANT SELECT (col1), INSERT (col1, col2), UPDATE (col2)\n    ON db_name.table_name\n    TO ercan@localhost;\n```\n---\nAccess a table:\n```mysql\nUSE database_name;\nSELECT * FROM table_name;\n\nOR\n\nSELECT * FROM database_name.table_name;\n```\n---\nCreate a table:\n```mysql\nCREATE TABLE table_name(\n    id INT PRIMARY KEY AUTO_INCREMENT, # primary key is auto indexed, only one column can auto increment\n    name VARCHAR(40) NOT NULL, # 40 character \n    json_column JSON # {\"key\": dynamic_value }\n)\n```\n---\nGet table information:\n```mysql\nDESCRIBE table_name;\n```\n---\n```mysql\nALTER TABLE ... \n```\n- Columns can be added to the table, first or after a certain column.\n- Column type can be changed with MODIFY.\n- Column can be drop in the table.\n- Column name can be changed.\n- Table name can be changed. // or RENAME new_table_name TO old_table_name;\n---\n```mysql\nTRUNCATE TABLE ... \n```\n- Deletes all values of the table but not the table. First the table is deleted and then recreated.\n---\nCopy Table:\n```mysql\nCREATE {TEMPORARY} TABLE IF NOT EXISTS duplicate_table SELECT * FROM original_table;\n```\n---\n```yaml\nTable Locking:\n    LOCK TABLES table_name [READ | WRITE];\n    READ LOCK: Without the read lock released, other sessions cannot write, but they can read.\n    there is no locking in their own session, they do this for other sessions.\n    WRITE LOCK: Blocks both writing and reading for other sessions.\n```\n---\n```yaml\nUser Locking:\n  Y: Locked\n  N: Unlocked\n  Locking:\n  \u003e CREATE USER IF NOT EXISTS ercan@localhost\n    IDENTIFIED BY '123'\n    ACCOUNT LOCK;\n  \u003e SELECT user, host, account_locked\n    FROM mysql.user\n    WHERE user = 'ercan' AND host = 'localhost';// returns 'Y' on account_locked column\nAfter trying to login it returns access denied.\n  Unlocking:\n  \u003e ALTER USER [IF EXISTS] 'ercan'@'localhost' ACCOUNT UNLOCK;\n```\n---\nInsert:\n```mysql\nINSERT INTO table_name(name) VALUES ('Ercan');\nINSERT INTO table_name('name', )\n```\n---\nUpdate:\n```mysql\nUPDATE table_name \n    SET column_name = \"new value\"\n    WHERE table_name.id = 123;\n```\n---\nSub-queries:\n```mysql\nSELECT * FROM product p WHERE p.name = (SELECT product_name FROM order WHERE order_id = 123 LIMIT 1);\n```\n---\nCHECK constraints:\n```mysql\n    Name varchar(45) CHECK(Name!=\"ercan\"),\n    - Name should not be \"ercan\" otherwise you cannot add \n    \n    Name VARCHAR(30) DEFAULT \"ERCAN\",\n    - If the Name is empty, the default is used.\n    \n    category_id int,\n    FOREIGN KEY (category_id) REFERENCES Category(id) \n        ON DELETE CASCADE \n        ON UPDATE CASCADE\n```\n---\n```mysql\nREPLACE INTO table_name SET column1 = value1, column2 = value2;\n```\nThis command is not safe. First deletes the matching rows then replaces the values\n\n---\nINSERT IGNORE:\n```mysql\nINSERT INTO table_name(column_name) VALUES (\"ercan\"); # returns error because column_name VARCHAR(3)\nINSERT IGNORE INTO table_name(column_name) VALUES (\"ercan\"); # done!\n```\n\nor \\\nif column_name is UNIQUE and we try to adding the same data, the previously added one becomes the update with new.\n---\nINSERT INTO SELECT:\n```mysql\nINSERT INTO user_info (name, email)\nSELECT name, email\nFROM user\nWHERE age = 33;\n```\n---\n\u003ch3\u003eMYSQL INDEX\u003ch3/\u003e\nPRIMARY and UNIQUEs automatically indexed.\ninnoDB use the binary search;\n```mysql\nCREATE INDEX index_name ON employees(lastName, firstName);\n\nwe can use query optimizer when lastName in the \"where\".\n-\nwe can use query optimizer when lastName and firstName in the \"where\".\n-\nwe can not use query optimizer when only firstName in the \"where\".\n-\nwe can not use query optimizer when lastName and firstName in the \"where\",\n    if they are connected by \"or\" instead of \"and\".\n\nEx-2:\n    ... ON employees(col1, col2, col3);\n    query optimizer run:\n        col1\n        col1, col2\n        col1, col2, col3,\n    query optimizer not run:\n        col2, col3\n        col1, col3\n```\nDROP INDEX:\n```mysql\nDROP INDEX index_name ON table_name [algorithm_option | lock_option];\n```\ndefault algorithm: INPLACE. If INPLACE is not supported, it uses the COPY algorithm.\n\nClustered and Non-Clustered Index:\n\u003cbr\u003e\nPRIMARY KEY is Clustered index and sorts the rows automatically. One in each table.\nAll indexes except clustered index are Non-Clustered index.\n--- \n```mysql\nSELECT IF(11=11, \"Equal\", \"Not equal\");\nSELECT IFNULL(phone_number, \"+905555555555\") as FROM user;\n```\n---\n```mysql\nWHERE col1 IN (...)      =   WHERE col1 = ANY(...)\nWHERE col1 \u003c\u003eANY (...)   =   WHERE col1 NOT IN (...)\n    -SOME = ANY-\n    \"\u003c\u003e\" = \"!=\"   \np.price \u003e ALL(50, 60) # p.price \u003e 50 and p.price \u003e 60\n```\n---\n```mysql\nSELECT * FROM user INNER JOIN  user_info ON user.id = user_info.id;\n                                    ...  USING(id) WHERE name IS NOT NULL;\nRIGHT/LEFT [OUTER] JOIN \nSELF JOIN # To join a table with itself.\nEquiJoin -\u003e # Used when combining 3 tables. actually called EquiJoin.\nNATURAL JOIN # It joins the tables based on the same column names and their data types\n\n```\n---\n```mysql\nWHERE [NOT] EXISTS ... # returns 1 or 0\nQuery will run if \"EXISTS\" return 1, so can run row specific\n```\n---\nGROUP_CONCAT:\n```mysql\nSELECT id, name,\n       GROUP_CONCAT(hobby) as \"hobbies\" FROM user group by id;\n```\n---\nRLIKE = REGEXP_LIKE \\\n\"_\" -\u003e for one char \\\n\"%\" -\u003e includes all characters before or after the characters\n---\n```mysql\nSELECT engine FROM information_schema.tables ... \n```\n---\nYou can export table to CSV\n\n---\nThe LAG() function is used to get value from row that precedes the current row.\\\nThe LEAD() function is used to get value from row that succeeds the current row. \n---\nCTE(Common Table Expression) : \\\n    The result set of a query that exists temporarily and is typically recursive and for use in large query expressions.\\\n    CTE exists within a transaction and then disappears\\\nView is a stored SQL query. It doesn't store the output of the query, it just stores the query\n\n```mysql\nWITH kids AS (  \nSELECT * FROM users WHERE age \u003c 8   \n)   \nSELECT name, surname, age FROM kids  \nWHERE name = 'John' and surname = 'Doe' ORDER BY age;\n\n```\n---\n```mysql\n...\nFOREIGN KEY (product_id) REFERENCES Product (id) \n    ON DELETE CASCADE\n    ON UPDATE CASCADE\n...\n```\nMySQL contains five different referential options: # default : RESTRICT, If ON DELETE ... and ON UPDATE ... values are not added\\\n1- CASCADE:\n    Used to automatically delete or update matching records from child table when we delete or update rows from parent table\\\n2- SET NULL:\n    It assigns NULL to the child's foreign key column\\\n3- RESTRICT:\n    If the parent row is deleted or updated, the child row is not deleted or updated. Gives an error message. For example \"The field .... in the row you are trying to delete is in use in another table\". This error will appear if you delete the parent row. If you delete the child row it is attached to, you will not get an error, which is what it should be. \\\n4- NO ACTION:\n    A keyword from standard SQL. In MySQL, equivalent to RESTRICT. It doesn't take any action. That is, even if the parent row is deleted or updated, no action is taken on the child row\\\n5- SET DEFAULT:\n    The MySQL parser recognizes this action. However, the InnoDB and NDB tables both rejected this action\n\n---\nUPSERT:\nIf a record is new, it will trigger an INSERT command. But, if it already exists in the table, then this operation will perform an UPDATE statement\n---\nPartitioning:\n1 - Horizontal Partitioning\n2 - Vertical Partitioning\n---\nSIGNAL RESIGNAL:\nUsed to return a warning or error message\n---\n```mysql\nSELECT FORMAT(13600.2021, 2, 'de_DE'); \n```\n---\nPrepared Statement\n * Better than normal statement.\n * Prepared statements are queries that contain the placeholders instead of actual values.\n * It can't be stored in database but we can use it with stored procedure\n```mysql\nPREPARE state FROM 'SELECT ?+? AS sum';\nSET @a = 10;\nSET @b = 20;\nEXECUTE state USING @a, @b;\n```\n---\nMINUS : first_table / second_table \\\nINTERSECT : first_table ∩ second_table \\\nCOALESCE :  return the first non-null value\n---\n```mysql\nSHOW PROCESSLIST\n```\nYou can see the work done by users instantly\n\n---\nFUNCTIONS\n * Stored in database\n * They can only return one value\n * using: custom_trace(2,4)\n * DETERMINISTIC: if it always gives the same result when given the same values\n```mysql\nDELIMITER $$ # The delimiter is changed to $ to enable the entire definition to be passed to the server as a single statement, and then restored to ; before invoking the procedure. This enables the ; delimiter used in the procedure body to be passed through to the server rather than being interpreted by mysql itself.\nCREATE FUNCTION custom_sum(\na INT,\nb INT \n) RETURNS INT\nDETERMINISTIC # or NOT DETERMINISTIC \nBEGIN \n   RETURN a+b;\nEND $$\nDELIMITER ;\n```\n---\nCURSORS\n * Reads the lines one by one\n```mysql\nDELIMITER $$\nCREATE PROCEDURE step_by_step()\nBEGIN \n    DECLARE orderCursor CURSOR FOR SELECT id, total_price FROM 'order';\n    DECLARE id INT;\n    DECLARE totalPrice FLOAT;\n\n    OPEN orderCursor;\n  \n    getOrder: LOOP\n        FETCH orderCursor INTO id, totalPrice;\n        IF id = 20 OR totalPrice = 333.2 THEN\n            ITERATE getOrder; # continue \n        ELSEIF totalPrice \u003e 999.3 THEN\n            LEAVE getOrder; # break\n        END IF;\n    END LOOP getOrder;\n    \n    CLOSE orderCursor;\nEND $$\nDELIMITER ;\n```\n---\nDECLARE ... HANDLER\n```mysql\nDELIMITER $$\nCREATE PROCEDURE handle_error()\nBEGIN \n    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION # CONTINUE: execution continues. EXIT: terminates between \"begin\" and \"end\"\n        BEGIN \n            # as catch function\n        END;\n    CALL get_error();\nEND $$\nDELIMITER ;\n```\n---\nSTORED PROCEDURES\n * Increases the performance, compiled and stored in database as soon as it is created\n * Secure, the procedure can be allowed without needing permission to access the tables\nEXAMPLE - 1 :\n```mysql\nDROP PROCEDURE IF EXISTS  get_categories; # if you don't use, you can't change the existing procedure\nDELIMITER $$\nCREATE PROCEDURE  get_categories()\nBEGIN \n   SELECT * FROM category;\nEND $$\nDELIMITER ;\n\n# using:\nCALL get_categories();\n```\nEXAMPLE - 2 :\n```mysql\nDROP PROCEDURE IF EXISTS get_categories;\nDELIMITER $$\nCREATE PROCEDURE custom_sum(\n    a INT, # default is IN. INOUT: \n    IN b INT, # IN : function parameter, only get\n    OUT c INT # OUT:  returned value, get and set. INOUT is like this\n)\nBEGIN \n    SELECT a+b INTO c;\nEND $$\nDELIMITER ;\n\n# using:\nSET @total = 0;\nCALL custom_sum(3, 3, @total);\nSELECT @total; # print 6\n```\n---\nVARIABLES\n- \u003cb\u003e User-Defined Variable Assignment: \u003c/b\u003e User-defined variables are created locally within a session and exist only within the context of that session\n```mysql\nSET @price := 10;\n--\nSET @price = 20;\n--\nSELECT @price := MAX(product.price)\nFROM product;\n```\n- \u003cb\u003e Parameter and Local Variable Assignment:\u003c/b\u003e SET applies to parameters and local variables in the context of the stored object within which they are defined\n```mysql\n...\nCREATE PROCEDURE ...\nBEGIN\n    DECLARE counter INT DEFAULT 0;\n    SET counter = counter + 10; # 0 + 10\nEND;\n...\n```\n- \u003cb\u003eSystem Variable Assignment: \u003c/b\u003e \n  * GLOBAL VARIABLE\n  * LOCAL VARIABLE for user session\n```mysql\nSET GLOBAL max_connections = 1000;\nSET @@GLOBAL.max_connections = 1000;\n--\nSET SESSION sql_mode = 'TRADITIONAL';\nSET LOCAL sql_mode = 'TRADITIONAL';\nSET @@SESSION.sql_mode = 'TRADITIONAL';\nSET @@LOCAL.sql_mode = 'TRADITIONAL';\nSET @@sql_mode = 'TRADITIONAL';\nSET sql_mode = 'TRADITIONAL';\n# All of the above are the same\n```\n---\nTRIGGER: https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html\n * It is run before (BEFORE) or after (AFTER) the INSERT, UPDATE, and DELETE operations.\n * If you drop a table, any triggers for the table are also dropped.\n * There should be no action on the same table in the trigger, otherwise it will be an infinite loop. And triggers should be created carefully to avoid circular loop\n * Within the trigger body, the OLD and NEW keywords enable you to access columns in the rows affected by a trigger. OLD and NEW are MySQL extensions to triggers; they are not case-sensitive.\n   - In an INSERT trigger, only NEW.col_name can be used; there is no old row.\n   - In a DELETE trigger, only OLD.col_name can be used; there is no new row.\n   - In an UPDATE trigger, you can use OLD.col_name to refer to the columns of a row before it is updated and NEW.col_name to refer to the columns of the row after it is updated.\nExample:\n```mysql\nDELIMITER $$\nCREATE TRIGGER product_update_stock AFTER INSERT ON order_detail FOR EACH ROW \nBEGIN \n    UPDATE product \n    SET stock_qty = stock_qty - NEW.order_qty\n    WHERE product.id = NEW.product_id;\nEND $$\nDELIMITER ;\n```\n---\nEVENTS(Cron Job on MySql)\n```mysql\nCREATE EVENT optimizer\nON SCHEDULE # AT timestamp is used for a one-time event.\n    EVERY 24 HOUR\n        STARTS # STARTS '2022-01-01' ENDS '2023-01-01'\n        DO OPTIMIZE TABLE 'order'; # That can be used to reclaim unused space\n```\n---\nTRANSACTION\n * It has the ability to undo operations made in it.\n * It is only supported in innoDB(for all standard types) and thus complies with the ACID database design principle. \n   * A tomic : They can not be disintegrated in a distress all operations are undone\n   * C onsistency : Database is a whole\n   * I solation : Operations are processed sequentially\n   * D urability : Relationship between physical resource and Mysql software\n\n```mysql\nSTART TRANSACTION;\n# queries\nROLLBACK; # you can undo changes if errors occur\n# queries\nCOMMIT; # to permanently apply operations to tables\n```\n * Autocommit is enabled on mysql, to disable autocommit mode:\n   * 1- For a single series of statements:\n     * SELECT @total:=SUM(salary) FROM employee WHERE type=1; \n   * 2- Can be written between begin-end\n   * 3- SET autocommit=0;\n   * 4- Add autocommit=0 to my.cnf file\n \n * Transaction Isolation Levels: # default transaction isolation level is Repeatable Read.\n   * can solve:     +\n   * can not solve: - \n\n| Isolation Levels \\ Concurrency Problems | Dirty Reads | Lost Updates | Non-Repeatable Reads | Phantom Reads |\n|:-----------------------------------------|-------------|--------------|:---------------------|:--------------|\n| READ UNCOMMITTED                         | -           | -            | -                    | -             |\n| READ COMMITTED                           | +           | -            | -                    | -             |\n| REPEATABLE READ                          | +           | +            | +                    | -             |\n| SERIALIZABLE                             | +           | +            | +                    | +             |\n\nREPEATABLE READ : It takes a copy of the current output of a query and uses it when called again, disregarding the changes of other sessions. This copy job degrades performance.\nSERIALIZABLE : In a session, it locks the rows used. In other sessions, these rows can only be read, not added or updated.\n * Dirty Reads : You can read uncommitted variables in another session. Afterwards, that variable may rollback, but the other session continues as it was.\n * Non-Repeatable Reads : Session A changes a data to 30 without committing, B accesses this data and reads 20, then after A commits, when B accesses this data again, this time it reads as 30\n * Lost Updates : Data a is now 0, increased by 1 unit in one session, and increased by 1 unit in the other session at the same time. The scenario where I see 1 when I should normally see 2.\n * Phantom Reads : I get the total number of products, another session deletes one of the products or adds a new one. So that product is phantom :)\n```mysql\nSET [PERSIST|GLOBAL] TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;\n```\n---\nFULLTEXT SEARCH\n * Full-text indexes can be created only for VARCHAR, CHAR or TEXT columns. \n * There are three types of full-text searches :\n   * Natural Language Full-Text Searches\n   * Boolean Full-Text searches\n   * Query expansion searches\n   \u003cbr\u003e\n   Natural Language Full-Text Searches:\n     * Default search type\n     * The rows returned are automatically sorted with the highest relevance first.\n     ```mysql\n     SELECT * FROM table_name WHERE MATCH(col1, col2) AGAINST('search terms' IN NATURAL LANGUAGE MODE)\n     # Returns lines containing \"search\", \"terms\" and \"search terms\" as case insensitive. By default, the search is case-insensitive\n     ```\n     * How to retrieve the relevance values explicitly:\n     ```mysql\n       SELECT id, MATCH(col1,col2) AGAINST ('search terms' IN NATURAL LANGUAGE MODE) AS score FROM table_name;\n     ``` \n   \u003cbr\u003e  \n   Boolean Full-Text searches\n   Example: # +, -, \"\", ~ etc. \n   \n   ```mysql\n        SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('+MySQL -YourSQL' IN BOOLEAN MODE);\n        # Get those that definitely contain the word \"MySQL\" and those that definitely don't contain the word \"YourSQL\"\n   ```\n   \u003cbr\u003e\n\n   Query expansion searches \n   [Dev MySql](https://dev.mysql.com/doc/refman/8.0/en/fulltext-query-expansion.html) \n   \u003cbr\u003e\n   Could be searching for books by Georges Simenon about Maigret, when a user is not sure how to spell “Maigret”. A search for “Megre and the reluctant witnesses” finds only “Maigret and the Reluctant Witnesses” without query expansion. A search with query expansion finds all books with the word “Maigret” on the second pass.\n   ```mysql\n        SELECT * FROM table_name WHERE MATCH (title,body) AGAINST ('search terms' WITH QUERY EXPANSION);\n    ```\n---\n SQL Normalisation\n * UNF : Un-normalized Form \n * 1NF : 1st Normal Form\n * 2NF : 2nd Normal Form\n * 3NF : 3rd Normal Form\n * BCNF : Boyce-Codd(3.5NF)\n * 4NF \n * 5NF \n\n1NF:\n* Repeating columns cannot be found in the same table.\n* Each column can have only one value (don't combine commas and data).\n\n2NF:\n* Database must be 1NF\n* There must be a column that holds a unique value for each row.\n* Any subset of data should not be repeated in more than one row. New tables must be created for such subsets of data.\n* Relationships should be defined between main tables and new tables by using foreign keys.\n\n3NF:\n* Database must be 2NF\n* No non-key column should be dependent on one another or have any transitional functional dependency. In other words, each column must be fully dependent on the key.\n  - There is a col1 column and a col2 column; col1=10, col2=20, col3 column as total should not be 10+20=30  \n---\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F44437%2Fmysql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F44437%2Fmysql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F44437%2Fmysql/lists"}