Skip to content

Commit 084ea58

Browse files
committed
add a sql server example
1 parent 34dc52e commit 084ea58

File tree

11 files changed

+572
-10
lines changed

11 files changed

+572
-10
lines changed

Cargo.lock

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ panic = "abort"
1818
codegen-units = 2
1919

2020
[dependencies]
21-
sqlx = { package = "sqlx-oldapi", version = "0.6.35", features = [
21+
sqlx = { package = "sqlx-oldapi", version = "0.6.36", features = [
2222
"any",
2323
"runtime-actix-rustls",
2424
"sqlite",
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Handling json data in Microsoft SQL Server
2+
3+
This demonstrates both how to produce and read json data from a SQL query
4+
in MS SQL Server (or Azure SQL Database), for creating advanced forms.
5+
6+
This lets your user interact with your database with a simple web interface,
7+
even when you have multiple tables, with one-to-many relationships.
8+
9+
![](./screenshots/app.png)
10+
11+
## Documentation
12+
13+
SQLPage requires JSON to create multi-select input (dropdowns where an user can select multiple values).
14+
The result of these multi-selects is a JSON array, which also needs to be read by SQL queries.
15+
16+
This example demonstrates how to consume [JSON](https://en.wikipedia.org/wiki/JSON) data from a SQL Server database,
17+
using the [`OPENJSON`](https://docs.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql)
18+
function to parse the JSON data into a table,
19+
and [`FOR JSON PATH`](https://docs.microsoft.com/en-us/sql/t-sql/functions/for-json-sql-server)
20+
to format query results as a JSON array.
21+
22+
23+
This demonstrates an application designed for managing groups and users, allowing the creation of new groups, adding users, and assigning users to one or multiple groups.
24+
25+
The application has the following sections:
26+
27+
- **Create a New Group**: A form where users can enter the name of a new group.
28+
- **Groups Display**: A list of existing groups.
29+
- **Add a User**: A form where users can enter the name of a new user and select one or multiple groups to assign to this user.
30+
- **Users Display**: A list of existing users and their associated group memberships.
31+
32+
When users submit the form, their selections are packaged up and sent to the database server. The server receives these selections as a structured JSON array.
33+
34+
The database then takes this list of selections and temporarily converts it into a format it can work with using the `OPENJSON` function, before saving the information permanently in the database tables. This allows the system to process multiple selections at once in an efficient way.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
services:
2+
web:
3+
image: lovasoa/sqlpage:main
4+
ports:
5+
- "8080:8080"
6+
volumes:
7+
- .:/var/www
8+
- ./sqlpage:/etc/sqlpage
9+
depends_on:
10+
- db
11+
environment:
12+
RUST_LOG: sqlpage=debug
13+
DATABASE_URL: mssql://sa:YourStrong!Passw0rd@db:1433/
14+
db:
15+
ports:
16+
- "1433:1433"
17+
image: mcr.microsoft.com/mssql/server:2022-latest
18+
volumes:
19+
- ./sqlpage/mssql-migrations:/migrations
20+
environment:
21+
ACCEPT_EULA: Y
22+
MSSQL_SA_PASSWORD: YourStrong!Passw0rd
23+
MSSQL_PID: Express
24+
command: >
25+
bash -c "
26+
/opt/mssql/bin/sqlservr &
27+
until /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P YourStrong!Passw0rd -C -Q 'SELECT 1;'; do
28+
echo 'Waiting for database...'
29+
sleep 1
30+
done
31+
/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P YourStrong!Passw0rd -C -i /migrations/0001_db_init.sql
32+
tail -f /dev/null"
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
select 'form' as component, 'Create a new Group' as title, 'Create' as validate;
2+
select 'Name' as name;
3+
4+
insert into groups(name) select :Name where :Name is not null;
5+
6+
select 'list' as component, 'Groups' as title, 'No group yet' as empty_title;
7+
select name as title from groups;
8+
9+
select 'form' as component, 'Add a user' as title, 'Add' as validate;
10+
select 'UserName' as name, 'Name' as label;
11+
select
12+
'Memberships[]' as name,
13+
'Group memberships' as label,
14+
'select' as type,
15+
1 as multiple,
16+
'press ctrl to select multiple values' as description,
17+
(
18+
SELECT name as label, id as value
19+
FROM groups
20+
FOR JSON PATH -- this builds a JSON array of objects
21+
) as options;
22+
23+
insert into users(name) select :UserName where :UserName is not null;
24+
25+
insert into group_members(group_id, user_id)
26+
select json_elem.value, IDENT_CURRENT('users')
27+
from openjson(:Memberships) as json_elem
28+
where :Memberships is not null;
29+
30+
select 'list' as component, 'Users' as title, 'No user yet' as empty_title;
31+
select
32+
users.name as title,
33+
string_agg(groups.name, ', ') as description
34+
from users
35+
left join group_members on users.id = group_members.user_id
36+
left join groups on groups.id = group_members.group_id
37+
group by users.id, users.name;
122 KB
Loading
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
create table users (
2+
id int primary key IDENTITY(1,1),
3+
name varchar(255) not null
4+
);
5+
6+
create table groups (
7+
id int primary key IDENTITY(1,1),
8+
name varchar(255) not null
9+
);
10+
11+
create table group_members (
12+
group_id int not null,
13+
user_id int not null,
14+
constraint PK_group_members primary key (group_id, user_id),
15+
constraint FK_group_members_groups foreign key (group_id) references groups (id),
16+
constraint FK_group_members_users foreign key (user_id) references users (id)
17+
);
18+
19+
CREATE TABLE questions(
20+
id INT PRIMARY KEY IDENTITY(1,1),
21+
question_text TEXT
22+
);
23+
24+
CREATE TABLE survey_answers(
25+
id INT PRIMARY KEY IDENTITY(1,1),
26+
question_id INT,
27+
answer TEXT,
28+
timestamp DATETIME DEFAULT GETDATE(),
29+
CONSTRAINT FK_survey_answers_questions FOREIGN KEY (question_id) REFERENCES questions(id)
30+
);
31+
32+
INSERT INTO questions(question_text) VALUES
33+
('What is your name?'),
34+
('What is your age?'),
35+
('What is your favorite color?');
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Migrations for Microsoft SQL Server
2+
3+
This folder contains the migrations for the Microsoft SQL Server example.
4+
5+
At the time of writing, SQLPage does not support applying migrations for Microsoft SQL Server
6+
automatically, so we need to apply them manually.
7+
8+
We write the migrations in a folder called `mssql-migrations`, instead of the usual `migrations`
9+
folder, and we use the `sqlcmd` tool to apply them.
10+
11+
See [how it is done in the docker-compose file](../../docker-compose.yml).
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
SELECT 'form' as component, 'Survey' as title;
2+
SELECT id as name, question_text as label, 'textarea' as type
3+
FROM questions;
4+
5+
-- Save all the answers to the database, whatever the number and id of the questions
6+
INSERT INTO survey_answers (question_id, answer)
7+
SELECT
8+
question_id,
9+
json_unquote(
10+
json_extract(
11+
sqlpage.variables('post'),
12+
concat('$."', question_id, '"')
13+
)
14+
)
15+
FROM json_table(
16+
json_keys(sqlpage.variables('post')),
17+
'$[*]' columns (question_id int path '$')
18+
) as question_ids;
19+
20+
-- Show the answers
21+
select 'card' as component, 'Survey results' as title;
22+
select
23+
questions.question_text as title,
24+
survey_answers.answer as description,
25+
'On ' || survey_answers.timestamp as footer
26+
from survey_answers
27+
inner join questions on questions.id = survey_answers.question_id;

0 commit comments

Comments
 (0)