SQL-kategorier: DDL, DML, DQL, DCL och TCL

SQL-kommandon delas in i fem huvudkategorier beroende på vad de gör. Att förstå dessa kategorier hjälper dig att veta vilket verktyg du ska använda för olika uppgifter.

Innehållsförteckning

Översikt

KategoriVad gör den?Vanliga kommandon
DDLDefinierar strukturCREATE, ALTER, DROP, TRUNCATE
DMLÄndrar dataINSERT, UPDATE, DELETE
DQLHämtar dataSELECT
DCLStyr behörigheterGRANT, REVOKE
TCLKontrollerar transaktionerCOMMIT, ROLLBACK, SAVEPOINT

Notera: Vissa källor inkluderar SELECT i DML. Här separerar vi DQL (Data Query Language) för SELECT eftersom det är så centralt och skiljer sig från de andra DML-kommandona (SELECT läser, de andra skriver).

Minnesregel:

  • DDL = Define (schema/struktur)
  • DML = Modify (ändra data - INSERT/UPDATE/DELETE)
  • DQL = Query (hämta data - SELECT)
  • DCL = Control (säkerhet/rättigheter)
  • TCL = Transaction (commit/rollback)

DDL - Data Definition Language

DDL (Data Definition Language) definierar strukturen i databasen - tabeller, index, vyer, constraints.

Kommandon

CREATE - Skapa struktur

Skapa databas:

CREATE DATABASE MyShop;

Skapa tabell:

CREATE TABLE Customers (
    CustomerId INTEGER PRIMARY KEY AUTOINCREMENT,
    Name TEXT NOT NULL,
    Email TEXT UNIQUE NOT NULL,
    CreatedAt TEXT DEFAULT (datetime('now'))
);

Skapa index:

CREATE INDEX idx_customers_email ON Customers(Email);

Skapa vy:

CREATE VIEW ActiveCustomers AS
SELECT CustomerId, Name, Email
FROM Customers
WHERE IsActive = 1;

ALTER - Ändra struktur

Lägg till kolumn:

ALTER TABLE Customers
ADD COLUMN Phone TEXT;

Byt namn på tabell:

ALTER TABLE Customers
RENAME TO Users;

Lägg till constraint (SQLite-limitation):

-- SQLite stödjer inte ADD CONSTRAINT direkt
-- Måste återskapa tabell med ny struktur
CREATE TABLE CustomersNew (
    CustomerId INTEGER PRIMARY KEY,
    Name TEXT NOT NULL,
    Email TEXT UNIQUE NOT NULL,
    CHECK (Email LIKE '%@%.%')  -- Ny constraint
);

INSERT INTO CustomersNew SELECT * FROM Customers;
DROP TABLE Customers;
ALTER TABLE CustomersNew RENAME TO Customers;

DROP - Ta bort struktur

Ta bort tabell:

DROP TABLE IF EXISTS OldCustomers;

Ta bort index:

DROP INDEX IF EXISTS idx_old_index;

Ta bort vy:

DROP VIEW IF EXISTS OldView;

Varning: DROP är permanent - all data försvinner!

TRUNCATE - Töm tabell

SQLite: Använd DELETE (TRUNCATE finns inte)

-- Töm tabell (behåll struktur)
DELETE FROM TempData;

-- Återställ AUTOINCREMENT
DELETE FROM sqlite_sequence WHERE name = 'TempData';

SQL Server / PostgreSQL:

TRUNCATE TABLE TempData;  -- Snabbt, återställer AUTOINCREMENT

DDL Egenskaper

Auto-commit - DDL-kommandon sparas direkt (kan inte ROLLBACK i de flesta databaser) ✓ Schema-ändringar - Påverkar struktur, inte data ✓ Metadata - Sparas i systemtabeller (sqlite_master, information_schema)

Viktigt i SQLite:

  • DDL körs oftast UTANFÖR transaktioner (auto-commit)
  • ALTER TABLE har begränsningar (kan inte ta bort kolumner direkt)
  • Använd migrations för komplexa schema-ändringar

DML - Data Manipulation Language

DML (Data Manipulation Language) ändrar innehållet i tabellerna - lägga till, ändra, ta bort data.

Notera: Traditionellt inkluderar DML även SELECT, men vi separerar det till DQL (se nedan) eftersom SELECT är read-only medan övriga DML-kommandon skriver data.

Kommandon

INSERT - Lägga till data

Enkel insert:

INSERT INTO Customers (Name, Email)
VALUES ('Anna Andersson', 'anna@example.com');

Flera rader:

INSERT INTO Customers (Name, Email) VALUES
    ('Bengt Bengtsson', 'bengt@example.com'),
    ('Cecilia Carlsson', 'cecilia@example.com'),
    ('David Davidsson', 'david@example.com');

Från SELECT:

INSERT INTO ArchivedOrders
SELECT * FROM Orders
WHERE OrderDate < '2020-01-01';

UPDATE - Ändra data

Enkel update:

UPDATE Customers
SET Email = 'anna.new@example.com'
WHERE CustomerId = 1;

Uppdatera flera kolumner:

UPDATE Products
SET Price = Price * 1.10,
    UpdatedAt = datetime('now')
WHERE CategoryId = 5;

Med JOIN (SQLite-syntax):

UPDATE Products
SET CategoryName = (
    SELECT Name FROM Categories
    WHERE Categories.CategoryId = Products.CategoryId
);

DELETE - Ta bort data

Enkel delete:

DELETE FROM Customers
WHERE CustomerId = 999;

Med villkor:

DELETE FROM Orders
WHERE OrderDate < '2020-01-01'
  AND Status = 'Cancelled';

Ta bort allt (behåll struktur):

DELETE FROM TempData;  -- Ta bort alla rader

DML Egenskaper

Transaktionell - Kan ROLLBACK (om i transaktion) ✓ Data-ändringar - Påverkar innehåll, inte struktur ✓ Logging - Loggas för ångerbarhet ✓ Skriver data - Ändrar innehållet (INSERT/UPDATE/DELETE)

Viktigt:

  • DML kan köras i transaktioner (BEGIN TRANSACTION, COMMIT, ROLLBACK)
  • INSERT/UPDATE/DELETE ändrar data
  • För att LÄSA data, se DQL (SELECT)

DQL - Data Query Language

DQL (Data Query Language) hämtar data från tabellerna utan att ändra något. DQL består i praktiken endast av SELECT-kommandot, men det är så centralt att det förtjänar sin egen kategori.

Varför separera DQL från DML?

SELECT skiljer sig fundamentalt:

  • Read-only - Ändrar ALDRIG data
  • Ingen logging - Behöver inte loggas för ROLLBACK
  • Ingen locking (vanligtvis) - Blockerar inte skrivoperationer
  • Mest använd - 80-90% av alla SQL-queries är SELECT

DML (INSERT/UPDATE/DELETE) däremot:

  • ✗ Skriver data
  • ✗ Kräver logging
  • ✗ Låser rader
  • ✗ Kan ROLLBACK

SELECT - Hämta data

Enkel select:

SELECT Name, Email FROM Customers;

Alla kolumner:

SELECT * FROM Customers;

Med villkor (WHERE):

SELECT * FROM Customers
WHERE IsActive = 1
ORDER BY Name;

Med JOIN:

SELECT c.Name, o.OrderDate, o.Total
FROM Customers c
INNER JOIN Orders o ON c.CustomerId = o.CustomerId
WHERE o.Total > 1000;

Med aggregering:

SELECT
    CategoryId,
    COUNT(*) AS ProductCount,
    AVG(Price) AS AvgPrice,
    MAX(Price) AS MaxPrice
FROM Products
GROUP BY CategoryId
HAVING COUNT(*) > 5;

Med subquery:

SELECT Name FROM Customers
WHERE CustomerId IN (
    SELECT CustomerId FROM Orders
    WHERE OrderDate > '2024-01-01'
);

Med UNION:

SELECT Name, 'Customer' AS Type FROM Customers
UNION
SELECT Name, 'Supplier' AS Type FROM Suppliers;

Med CTE (Common Table Expression):

WITH HighValueCustomers AS (
    SELECT CustomerId, SUM(Total) AS TotalSpent
    FROM Orders
    GROUP BY CustomerId
    HAVING SUM(Total) > 10000
)
SELECT c.Name, h.TotalSpent
FROM Customers c
JOIN HighValueCustomers h ON c.CustomerId = h.CustomerId;

DQL Egenskaper

Read-only - Ändrar aldrig data ✓ Transaktionsoberoende - Behöver inte COMMIT ✓ Ingen locking (default) - Blockerar inte andra queries ✓ Optimerbar - Query planner kan optimera ✓ Cacheable - Resultat kan cachas

Viktigt för SELECT:

  • SELECT returnerar ResultSet (rader/kolumner)
  • Kan vara långsam på stora tabeller (använd index!)
  • ORDER BY sorterar (extra kostnad)
  • DISTINCT tar bort dubbletter (extra kostnad)
  • COUNT/SUM/AVG kräver scan av alla rader (långsamt utan index)

Prestanda-tips för DQL

DÅLIGT ❌:

-- Hämtar ALLA kolumner (även oanvända)
SELECT * FROM Products;

-- Ingen WHERE = full table scan
SELECT Name FROM Products;

-- COUNT(*) på stor tabell utan index
SELECT COUNT(*) FROM LargeTable;

BRA ✓:

-- Hämta bara nödvändiga kolumner
SELECT ProductId, Name FROM Products;

-- Använd WHERE för att filtrera tidigt
SELECT Name FROM Products WHERE CategoryId = 5;

-- Använd index för COUNT
SELECT COUNT(*) FROM Products WHERE CategoryId = 5;  -- Om index på CategoryId

DCL - Data Control Language

DCL (Data Control Language) styr behörigheter och säkerhet - vem får göra vad.

Kommandon

GRANT - Ge rättigheter

SQLite: SQLite har INGEN inbyggd säkerhet (fil-baserad). DCL fungerar inte!

SQL Server / PostgreSQL / MySQL:

-- Ge läsrättigheter
GRANT SELECT ON Customers TO user_readonly;

-- Ge alla rättigheter på tabell
GRANT ALL PRIVILEGES ON Orders TO user_admin;

-- Ge specifika rättigheter
GRANT SELECT, INSERT, UPDATE ON Products TO user_sales;

REVOKE - Ta bort rättigheter

-- Ta bort läsrättigheter
REVOKE SELECT ON Customers FROM user_readonly;

-- Ta bort alla rättigheter
REVOKE ALL PRIVILEGES ON Orders FROM user_admin;

DCL i SQLite

SQLite har INGEN DCL!

  • Hela databasen är en fil
  • Säkerhet hanteras på fil-nivå (OS permissions)
  • Använd filsystem-rättigheter (chmod, ACL) istället

Exempel (Linux/Mac):

# Endast owner kan läsa/skriva
chmod 600 mydb.db

# Alla kan läsa, endast owner kan skriva
chmod 644 mydb.db

Exempel (Windows):

  • Högerklicka fil → Properties → Security
  • Sätt läs/skriv-rättigheter per användare

DCL i produktionsdatabaser

Best practices:

-- Läsanvändare (applikation)
CREATE USER app_readonly WITH PASSWORD 'secure_pass';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;

-- Skrivanvändare (applikation)
CREATE USER app_writer WITH PASSWORD 'secure_pass';
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_writer;

-- Admin (DBA)
CREATE USER dba_admin WITH PASSWORD 'secure_pass';
GRANT ALL PRIVILEGES ON DATABASE mydb TO dba_admin;

Princip om minsta privilegium: Ge endast de rättigheter som behövs!

TCL - Transaction Control Language

TCL (Transaction Control Language) kontrollerar transaktioner - commit, rollback, savepoints.

Kommandon

BEGIN TRANSACTION - Starta transaktion

BEGIN TRANSACTION;
-- eller kort
BEGIN;

COMMIT - Spara ändringar permanent

BEGIN TRANSACTION;

INSERT INTO Accounts (AccountId, Balance) VALUES (1, 1000);
INSERT INTO Accounts (AccountId, Balance) VALUES (2, 2000);

COMMIT;  -- Ändringar sparas permanent

ROLLBACK - Ångra ändringar

BEGIN TRANSACTION;

UPDATE Accounts SET Balance = Balance - 500 WHERE AccountId = 1;
UPDATE Accounts SET Balance = Balance + 500 WHERE AccountId = 2;

-- Något gick fel!
ROLLBACK;  -- Ångra ALLA ändringar sedan BEGIN

SAVEPOINT - Skapa återställningspunkt

SQLite stödjer SAVEPOINT:

BEGIN TRANSACTION;

INSERT INTO Orders (CustomerId, Total) VALUES (1, 500);
SAVEPOINT order_created;

INSERT INTO OrderLines (OrderId, ProductId, Qty) VALUES (1, 101, 2);
SAVEPOINT lines_added;

INSERT INTO OrderLines (OrderId, ProductId, Qty) VALUES (1, 102, 1);

-- Något gick fel med sista raden!
ROLLBACK TO SAVEPOINT lines_added;  -- Ångra bara sista INSERT

-- Fortsätt arbeta...
INSERT INTO OrderLines (OrderId, ProductId, Qty) VALUES (1, 103, 3);

COMMIT;  -- Spara allt utom den rad vi rullade tillbaka

RELEASE SAVEPOINT:

RELEASE SAVEPOINT order_created;  -- Släpp savepoint (kan inte längre rollback till den)

Praktiskt exempel: Banköverföring

BEGIN TRANSACTION;

-- Kontrollera saldo
SELECT Balance FROM Accounts WHERE AccountId = 1;  -- 1000 kr

-- Dra från första kontot
UPDATE Accounts SET Balance = Balance - 500 WHERE AccountId = 1;

-- Kontrollera att det gick bra
SELECT Balance FROM Accounts WHERE AccountId = 1;  -- 500 kr

-- Lägg till på andra kontot
UPDATE Accounts SET Balance = Balance + 500 WHERE AccountId = 2;

-- Allt OK? Spara!
COMMIT;

Med felhantering:

BEGIN TRANSACTION;

-- Savepoint innan kritisk operation
SAVEPOINT before_transfer;

UPDATE Accounts SET Balance = Balance - 500 WHERE AccountId = 1;

-- Kontrollera att saldo inte blev negativt
SELECT Balance FROM Accounts WHERE AccountId = 1;

-- Om negativt: ROLLBACK TO SAVEPOINT before_transfer
-- Om OK: fortsätt

UPDATE Accounts SET Balance = Balance + 500 WHERE AccountId = 2;

COMMIT;

TCL Egenskaper

ACID-garantier:

  • Atomicity - Allt eller inget
  • Consistency - Data förblir giltigt
  • Isolation - Transaktioner påverkar inte varandra
  • Durability - Committed data överlever krascher

Viktigt:

  • Transaktioner låser rader (kan orsaka deadlocks)
  • Håll transaktioner korta (minimera lock-tid)
  • COMMIT sparar, ROLLBACK ångrar
  • SQLite: En skrivare i taget (databas-nivå lås)

Jämförelse och användning

När använder jag vilken?

UppgiftKategoriKommando
Skapa ny tabellDDLCREATE TABLE
Lägga till kolumnDDLALTER TABLE ADD
Lägga till dataDMLINSERT
Hämta dataDQLSELECT
Ändra dataDMLUPDATE
Ta bort data (behåll tabell)DMLDELETE
Ta bort tabell (allt)DDLDROP TABLE
Räkna antal raderDQLSELECT COUNT(*)
JOIN flera tabellerDQLSELECT … JOIN
Ge användare läsrättigheterDCLGRANT SELECT
Spara ändringarTCLCOMMIT
Ångra ändringarTCLROLLBACK

Interaktion mellan kategorier

-- DDL: Skapa struktur
CREATE TABLE Products (
    ProductId INTEGER PRIMARY KEY,
    Name TEXT NOT NULL,
    Price REAL NOT NULL
);

-- DML: Lägg till data
INSERT INTO Products (Name, Price) VALUES ('Laptop', 9999);

-- DQL: Hämta data
SELECT * FROM Products;

-- TCL: Starta transaktion
BEGIN TRANSACTION;

-- DML: Ändra data
UPDATE Products SET Price = Price * 1.10;

-- DQL: Kontrollera ändring
SELECT Name, Price FROM Products;

-- TCL: Spara eller ångra
COMMIT;  -- eller ROLLBACK

-- DCL: Ge rättigheter (ej SQLite)
GRANT SELECT ON Products TO user_sales;

-- DDL: Ändra struktur
ALTER TABLE Products ADD COLUMN Stock INTEGER DEFAULT 0;

-- DQL: Hämta uppdaterat schema
SELECT * FROM Products;

Praktiska exempel

Exempel 1: Skapa och fylla databas

-- DDL: Skapa schema
CREATE TABLE Customers (
    CustomerId INTEGER PRIMARY KEY AUTOINCREMENT,
    Name TEXT NOT NULL,
    Email TEXT UNIQUE NOT NULL
);

CREATE TABLE Orders (
    OrderId INTEGER PRIMARY KEY AUTOINCREMENT,
    CustomerId INTEGER NOT NULL,
    Total REAL NOT NULL,
    FOREIGN KEY (CustomerId) REFERENCES Customers(CustomerId)
);

-- DML: Lägg till testdata
INSERT INTO Customers (Name, Email) VALUES
    ('Anna', 'anna@example.com'),
    ('Bengt', 'bengt@example.com');

INSERT INTO Orders (CustomerId, Total) VALUES
    (1, 500),
    (1, 750),
    (2, 1200);

-- DML: Hämta resultat
SELECT
    c.Name,
    COUNT(o.OrderId) AS OrderCount,
    SUM(o.Total) AS TotalSpent
FROM Customers c
LEFT JOIN Orders o ON c.CustomerId = o.CustomerId
GROUP BY c.CustomerId;

Exempel 2: Migrera schema med data

-- DDL: Skapa ny tabell med bättre struktur
CREATE TABLE CustomersNew (
    CustomerId INTEGER PRIMARY KEY,
    FirstName TEXT NOT NULL,
    LastName TEXT NOT NULL,
    Email TEXT UNIQUE NOT NULL
);

-- DML: Kopiera data (dela upp Name)
INSERT INTO CustomersNew (CustomerId, FirstName, LastName, Email)
SELECT
    CustomerId,
    substr(Name, 1, instr(Name, ' ') - 1) AS FirstName,
    substr(Name, instr(Name, ' ') + 1) AS LastName,
    Email
FROM Customers;

-- DDL: Byt ut gamla tabellen
DROP TABLE Customers;
ALTER TABLE CustomersNew RENAME TO Customers;

Exempel 3: Säker transaktion med felhantering

-- TCL: Starta transaktion
BEGIN TRANSACTION;

-- DML: Kritisk operation
UPDATE Accounts SET Balance = Balance - 1000 WHERE AccountId = 1;

-- Kontrollera resultat
SELECT Balance FROM Accounts WHERE AccountId = 1;
-- Om negativt → ROLLBACK
-- Om OK → fortsätt

UPDATE Accounts SET Balance = Balance + 1000 WHERE AccountId = 2;

-- TCL: Spara
COMMIT;

Best practices

DDL Best Practices

Versionshantera schema:

-- migrations/001_create_customers.sql
CREATE TABLE Customers (...);

-- migrations/002_add_phone.sql
ALTER TABLE Customers ADD COLUMN Phone TEXT;

Använd IF EXISTS:

DROP TABLE IF EXISTS OldTable;
CREATE TABLE IF NOT EXISTS NewTable (...);

Backup innan stora ändringar:

# SQLite backup
sqlite3 mydb.db ".backup backup_$(date +%Y%m%d).db"

DML Best Practices

Alltid använd WHERE i UPDATE/DELETE:

-- DÅLIGT ❌
UPDATE Products SET Price = 100;  -- Uppdaterar ALLA!

-- BRA ✓
UPDATE Products SET Price = 100 WHERE ProductId = 5;

Testa SELECT innan DELETE:

-- Kör först för att se vad som tas bort
SELECT * FROM Orders WHERE Status = 'Cancelled';

-- Sen DELETE
DELETE FROM Orders WHERE Status = 'Cancelled';

Använd LIMIT vid testning:

-- Testa på en rad först
UPDATE Products SET Price = Price * 1.10 LIMIT 1;

TCL Best Practices

Håll transaktioner korta:

-- DÅLIGT ❌
BEGIN;
SELECT * FROM BigTable;  -- Långsam query
-- Användaren tänker...
UPDATE ...;
COMMIT;  -- Låser länge!

-- BRA ✓
-- Läs UTANFÖR transaktion
SELECT * FROM BigTable;

-- Kort transaktion för ändring
BEGIN;
UPDATE ...;
COMMIT;

Explicit COMMIT/ROLLBACK:

-- DÅLIGT ❌
BEGIN;
INSERT ...;
-- Glömde COMMIT!

-- BRA ✓
BEGIN;
try {
    INSERT ...;
    COMMIT;
}
catch {
    ROLLBACK;
    throw;
}

DCL Best Practices

Minsta privilegium:

-- DÅLIGT ❌
GRANT ALL PRIVILEGES ON DATABASE mydb TO app_user;

-- BRA ✓
GRANT SELECT, INSERT, UPDATE ON customers TO app_user;

Separata användare för olika roller:

CREATE USER app_readonly;  -- SELECT
CREATE USER app_writer;    -- SELECT, INSERT, UPDATE, DELETE
CREATE USER dba_admin;     -- ALL

TL;DR

SQL-kategorier:

KategoriSyfteKommandon
DDLDefiniera strukturCREATE, ALTER, DROP, TRUNCATE
DMLÄndra dataINSERT, UPDATE, DELETE
DQLHämta dataSELECT
DCLStyra behörigheterGRANT, REVOKE
TCLKontrollera transaktionerCOMMIT, ROLLBACK, SAVEPOINT

Minnesregler:

  • DDL = Schema/struktur (tabeller, index)
  • DML = Modify data (INSERT/UPDATE/DELETE - SKRIVER)
  • DQL = Query data (SELECT - LÄSER)
  • DCL = Säkerhet/rättigheter (ej SQLite!)
  • TCL = Transaktioner (commit/rollback)

Viktiga skillnader:

  • DDL = Auto-commit (kan inte ROLLBACK)
  • DML = Transaktionell, skriver data (kan ROLLBACK)
  • DQL = Read-only, ingen locking (kan inte ROLLBACK)
  • DCL = Ej i SQLite (fil-baserad säkerhet)
  • TCL = ACID-garantier

Varför separera DQL från DML?

  • SELECT är read-only (ändrar inget)
  • SELECT är 80-90% av alla queries
  • SELECT kräver ingen logging/locking
  • INSERT/UPDATE/DELETE skriver data

Best practices:

  • ✓ Versionshantera DDL (migrations)
  • ✓ Alltid WHERE i UPDATE/DELETE
  • ✓ Testa SELECT innan DELETE
  • ✓ Håll transaktioner korta
  • ✓ Explicit COMMIT/ROLLBACK
  • ✓ Minsta privilegium (DCL)

Exempel:

-- DDL: Skapa struktur
CREATE TABLE Products (ProductId INTEGER PRIMARY KEY, Name TEXT);

-- DML: Lägg till data
INSERT INTO Products (Name) VALUES ('Laptop');

-- DQL: Hämta data
SELECT * FROM Products;

-- TCL: Transaktion
BEGIN;
UPDATE Products SET Name = 'Gaming Laptop' WHERE ProductId = 1;  -- DML
SELECT Name FROM Products WHERE ProductId = 1;  -- DQL (kontrollera)
COMMIT;

-- DDL: Ta bort
DROP TABLE Products;

Kom ihåg:

  • DDL = Struktur (CREATE/ALTER/DROP)
  • DML = Ändra (INSERT/UPDATE/DELETE)
  • DQL = Läsa (SELECT)
  • DCL = Rättigheter (GRANT/REVOKE)
  • TCL = Transaktioner (COMMIT/ROLLBACK)

Förstå kategorierna → Använd rätt verktyg för rätt uppgift! 🎯


title: SQL — kategorier och innehåll

Vanliga kategorier i kursen:

  • Basics (DDL, DML)
  • Joins & Aggregation
  • Index & Performance
  • Transactions & Concurrency
  • Security & Backups

Exempel (skapande av kategori-tabell)

CREATE TABLE Categories (
    CategoryId INTEGER PRIMARY KEY,
    CategoryName TEXT NOT NULL UNIQUE,
    Description TEXT
);

INSERT INTO Categories (CategoryName, Description) VALUES ('Joins', 'Joins och relationer');

Upp

Upp


Licens: Apache 2.0 | © 2023 Marcus Medina, Campus Mölndal. Alla rättigheter förbehållna.
Du får använda och modifiera detta verk enligt villkoren i Apache License, Version 2.0. Du får inte använda detta verk för kommersiella ändamål utan tillstånd från upphovsmannen.