How to insert multiple row values into SQL - sql

I have the following 3 tables:
CREATE TABLE Tests (
Test_ID INT,
TestName VARCHAR(50));
INSERT INTO Tests VALUES (1, 'SQL Test');
INSERT INTO Tests VALUES (2, 'C# Test');
INSERT INTO Tests VALUES (3, 'Java Test');
CREATE TABLE Users (
[User_ID] INT,
UserName VARCHAR(50));
INSERT INTO Users VALUES (1, 'Joe');
INSERT INTO Users VALUES (2, 'Jack');
INSERT INTO Users VALUES (3, 'Jane');
CREATE TABLE UserTests (
ID INT,
[User_ID] INT,
Test_ID INT,
Completed INT);
INSERT INTO UserTests VALUES (1, 1, 1, 0);
INSERT INTO UserTests VALUES (2, 1, 2, 1);
INSERT INTO UserTests VALUES (3, 1, 3, 1);
INSERT INTO UserTests VALUES (4, 2, 1, 0);
INSERT INTO UserTests VALUES (5, 2, 2, 0);
INSERT INTO UserTests VALUES (6, 2, 3, 0);
INSERT INTO UserTests VALUES (7, 3, 1, 1);
INSERT INTO UserTests VALUES (8, 3, 2, 1);
INSERT INTO UserTests VALUES (9, 3, 3, 1);
I would like to create some rule/trigger so that when a new user gets added to the Users table, an entry for each Test and that user's Id will get added to the UserTests table.
Something like this if the new user ID is 5:
INSERT dbo.UserTest
(USER_ID, TEST_ID, Completed)
VALUES
(5, SELECT TEST_ID FROM Tests, 0)
That syntax is of course wrong but to give an idea of what I expect to happen.
So I expect that statement to add these values to the UserTests table:
User ID| Test ID| Completed
5 | 1 | 0
5 | 2 | 0
5 | 3 | 0

You can use after trigger for user table.
Create Trigger tr_user on Users
After Insert
AS Begin
INSERT UserTest(USER_ID, TEST_ID, Completed)
Select I.USER_ID, t.TEST_ID, 0
From Inserted I, Tests t
END

Here's a SQL Fiddle that finds missing records and inserts them.
SQL Fiddle
The SELECT:
select u.user_id, t.test_id, 0 as Completed
from users u
cross join tests t
where not exists (
select 1
from usertests ut
where ut.user_id = u.user_id and ut.test_id = t.test_id)
Adding insert into UserTests (User_Id, Test_Id, Completed) before the select will insert these records.
You can add a user id on to the where clause to do it for a single user if required. It is re-runnable so it won't re-insert test ids for a user that already has them, but will add new ones if new tests are introduced.

Related

SQL Timeline Query

read already some post but was not able to find a solution yet.
I gota table which looks like this:
and I would like to transform this data, so that I got a line (or row) per ID and an entry per date which displays the Status. The value column does not change its value for the corresponding id.
or
I am currently not able to do it. Even without the value row/line.
CREATE TABLE test (
id INT,
date1 text,
status1 INT,
value1 INT
);
INSERT INTO test VALUES (1, '01.01.2022', 1, 60);
INSERT INTO test VALUES (2, '01.01.2022', 1, 30);
INSERT INTO test VALUES (3, '01.01.2022', 7, 90);
INSERT INTO test VALUES (1, '02.01.2022', 7, 60);
INSERT INTO test VALUES (2, '02.01.2022', 7, 30);
INSERT INTO test VALUES (3, '02.01.2022', 3, 90);
INSERT INTO test VALUES (1, '03.01.2022', 7, 60);
INSERT INTO test VALUES (2, '03.01.2022', 5, 30);
INSERT INTO test VALUES (3, '03.01.2022', 7, 90);
Based on your suggestions I tried:
SELECT *
FROM
(
SELECT id, value1
FROM test
) AS SourceTable
PIVOT(AVG(status1) FOR date1 IN(select DISTINCT date1
from test)) AS PivotTable;
But I can not find my error.
Schema (MySQL v8.0)
CREATE TABLE test (
id INT,
date text,
status INT,
value INT
);
INSERT INTO test VALUES (1, '01.01.2022', 1, 60);
INSERT INTO test VALUES (2, '01.01.2022', 1, 30);
INSERT INTO test VALUES (3, '01.01.2022', 7, 90);
INSERT INTO test VALUES (1, '02.01.2022', 7, 60);
INSERT INTO test VALUES (2, '02.01.2022', 7, 30);
INSERT INTO test VALUES (3, '02.01.2022', 3, 90);
INSERT INTO test VALUES (1, '03.01.2022', 7, 60);
INSERT INTO test VALUES (2, '03.01.2022', 5, 30);
INSERT INTO test VALUES (3, '03.01.2022', 7, 90);
Query #1
SELECT
ID,
MAX(VALUE) AS VALUE,
sum(CASE WHEN date = '01.01.2022' THEN status ELSE 0 END) AS '01.01.2022',
sum(CASE WHEN date = '02.01.2022' THEN status ELSE 0 END) AS '02.01.2022',
sum(CASE WHEN date = '03.01.2022' THEN status ELSE 0 END) AS '03.01.2022'
FROM test
GROUP BY ID;
ID
VALUE
01.01.2022
02.01.2022
03.01.2022
1
60
1
7
7
2
30
1
7
5
3
90
7
3
7
View on DB Fiddle

Get Distinct User Count Over Particular Time MDX QUERY

I need some mdx help.
Cube Details:
Measures.Users -> Distinct Count on Users.
I want to find an mdx equivalent of this query:
Select a.shopId , Month(TransactionDate) Month_Transaction,
Year(TransactionDate) Year_Transaction,
count(distinct b.UserID) UniqueUserCount
FROM [dbo].[shop] a
JOIN users b ON a.UserID = b.UserID
where TransactionDate >= '2018-01-01'
Group by a.shopId ,Month(TransactionDate), Year(TransactionDate)
This is what I have so far which produces unique count irrespective of date. I want unique count in the date range. Pls let me know how to achieve this ?
SELECT {
[Date].[Month].&[2020]&[2020-Q3]&[2020-09],
[Date].[Month].&[2020]&[2020-Q4]&[2020-10],
[Date].[Month].&[2020]&[2020-Q4]&[2020-11],
[Date].[Month].&[2020]&[2020-Q4]&[2020-12]
} ON COLUMNS, NON EMPTY
{
[ShopLocations].[Hierarchy].[Shop]
} ON ROWS
FROM [ShopperCube]
where (Measures.Users)
The built-in distinct count measure gives the most flexibility. It sounds like you already have one as Measure.Users? Is the measure group for Users connected to your dimensions for Date and ShopLocations?
To help get that working, I would review the Distinct Count pattern in the Many-to-Many Revolution paper. That approach gives a no-code solution that is more flexible and probably faster to run:
https://sqlbi.com/whitepapers/many2many
You can use the DistinctCount MDX function.
The official documentation is not very clear, but, the general principal is: You pass in a "Set" to this function to get the distinct values.
A sample MDX
WITH SET MySet AS
{ [Dim User].[User Id].Children }
MEMBER Measures.SetDistinctCount AS
DISTINCTCOUNT(MySet)
SELECT { Measures.SetDistinctCount, Measures.Amount } ON 0
, { [Dim Date].[Date Key].AllMembers } ON 1
FROM [Mine]
To validate this, the following is my setup:
Query result
Also, given the sample sql to create table and work with different data:
IF OBJECT_ID('FactTransaction') IS NOT NULL
DROP TABLE FactTransaction
GO
CREATE TABLE FactTransaction (ShopId INT, TransactionDateKey INT, UserId INT, Amount INT)
GO
IF OBJECT_ID('DimDate') IS NOT NULL
DROP TABLE DimDate
GO
CREATE TABLE DimDate(DateKey INT, FullDate DATE)
GO
IF OBJECT_ID('DimUser') IS NOT NULL
DROP TABLE DimUser
GO
CREATE TABLE DimUser(UserId INT, UserName VARCHAR(50))
GO
IF OBJECT_ID('DimShop') IS NOT NULL
DROP TABLE DimShop
GO
CREATE TABLE DimShop(ShopId INT, ShopName VARCHAR(50))
GO
--Shop 1
INSERT INTO FactTransaction values(1, 20210101, 1, 10)
INSERT INTO FactTransaction values(1, 20210101, 2, 5)
INSERT INTO FactTransaction values(1, 20210101, 3, 20)
INSERT INTO FactTransaction values(1, 20210102, 2, 10)
INSERT INTO FactTransaction values(1, 20210102, 4, 15)
INSERT INTO FactTransaction values(1, 20210103, 3, 5)
INSERT INTO FactTransaction values(1, 20210103, 4, 10)
INSERT INTO FactTransaction values(1, 20210103, 5, 20)
INSERT INTO FactTransaction values(1, 20210103, 1, 20)
--Shop 2
INSERT INTO FactTransaction values(2, 20210103, 2, 10)
INSERT INTO FactTransaction values(2, 20210103, 2, 5)
INSERT INTO FactTransaction values(2, 20210103, 2, 20)
GO
INSERT INTO DimDate VALUES(20210101, '2021-01-01')
INSERT INTO DimDate VALUES(20210102, '2021-01-02')
INSERT INTO DimDate VALUES(20210103, '2021-01-03')
GO
INSERT INTO DimUser VALUES(1, 'First')
INSERT INTO DimUser VALUES(2, 'Second')
INSERT INTO DimUser VALUES(3, 'Third')
INSERT INTO DimUser VALUES(4, 'Fourth')
INSERT INTO DimUser VALUES(5, 'Fifth')
GO
INSERT INTO DimShop VALUES(1, 'Shop 1')
INSERT INTO DimShop VALUES(2, 'Shop 2')
GO

Sample observations per group without replacement in SQL

Using the provided table I would like to sample let's say 2 users per day so that users assigned to the two days are different. Of course the problem I have is more sophisticated, but this simple example gives the idea.
drop table if exists test;
create table test (
user_id int,
day_of_week int);
insert into test values (1, 1);
insert into test values (1, 2);
insert into test values (2, 1);
insert into test values (2, 2);
insert into test values (3, 1);
insert into test values (3, 2);
insert into test values (4, 1);
insert into test values (4, 2);
insert into test values (5, 1);
insert into test values (5, 2);
insert into test values (6, 1);
insert into test values (6, 2);
The expected results would look like this:
create table results (
user_id int,
day_of_week int);
insert into results values (1, 1);
insert into results values (2, 1);
insert into results values (3, 2);
insert into results values (6, 2);
You can use window functions. Here is an example . . . although the details do depend on your database (functions for random numbers vary by database):
select t.*
from (select t.*, row_number() over (partition by day_of_week order by random()) as seqnum
from test t
) t
where seqnum <= 2;

SQLite: How to count references to each ROWID in a table, then insert the count

I need some help writing a SQL statement. I have two tables in a SQLite database:
CREATE TABLE users (
user_id INTEGER PRIMARY KEY,
item_id INTEGER
);
CREATE TABLE items (
item_id INTEGER PRIMARY KEY,
ref_count INTEGER
);
I'm seeking a SQLite statement that will be like the following pseudocode:
for each row in items
items[ row ].ref_count = SELECT COUNT(users.item_id)
FROM users
WHERE users.item_id=row;
It looks like you can use the REPLACE command as follows:
REPLACE INTO items (item_id, ref_count)
SELECT item_id, COUNT(*) AS ref_count
FROM users
GROUP BY item_id;
Test case:
INSERT INTO users VALUES (1, 1);
INSERT INTO users VALUES (2, 1);
INSERT INTO users VALUES (3, 1);
INSERT INTO users VALUES (4, 2);
INSERT INTO users VALUES (5, 2);
INSERT INTO users VALUES (6, 3);
Result after running the REPLACE query:
SELECT * FROM items;
item_id ref_co
------------ ------
1 3
2 2
3 1
Adding some further items:
INSERT INTO users VALUES (7, 1);
INSERT INTO users VALUES (8, 4);
INSERT INTO users VALUES (9, 4);
And after re-running the REPLACE query:
SELECT * FROM items;
item_id ref_co
------------ ------
1 4
2 2
3 1
4 2

Help with a SQL Query

My tables:
suggestions:
suggestion_id|title|description|user_id|status|created_time
suggestion_comments:
scomment_id|text|user_id|suggestion_id
suggestion_votes:
user_id|suggestion_id|value
Where value is the number of points assigned to a vote.
I'd like to be able to SELECT:
suggestion_id, title, the number of comments and the SUM of values for that suggestion.
sorted by SUM of values. LIMIT 30
Any ideas?
You may want to try using sub queries, as follows:
SELECT s.suggestion_id,
(
SELECT COUNT(*)
FROM suggestion_comments sc
WHERE sc.suggestion_id = s.suggestion_id
) num_of_comments,
(
SELECT SUM(sv.value)
FROM suggestion_votes sv
WHERE sv.suggestion_id = s.suggestion_id
) sum_of_values
FROM suggestions s;
Test case:
CREATE TABLE suggestions (suggestion_id int);
CREATE TABLE suggestion_comments (scomment_id int, suggestion_id int);
CREATE TABLE suggestion_votes (user_id int, suggestion_id int, value int);
INSERT INTO suggestions VALUES (1);
INSERT INTO suggestions VALUES (2);
INSERT INTO suggestions VALUES (3);
INSERT INTO suggestion_comments VALUES (1, 1);
INSERT INTO suggestion_comments VALUES (2, 1);
INSERT INTO suggestion_comments VALUES (3, 2);
INSERT INTO suggestion_comments VALUES (4, 2);
INSERT INTO suggestion_comments VALUES (5, 2);
INSERT INTO suggestion_comments VALUES (6, 3);
INSERT INTO suggestion_votes VALUES (1, 1, 3);
INSERT INTO suggestion_votes VALUES (2, 1, 5);
INSERT INTO suggestion_votes VALUES (3, 1, 1);
INSERT INTO suggestion_votes VALUES (1, 2, 4);
INSERT INTO suggestion_votes VALUES (2, 2, 2);
INSERT INTO suggestion_votes VALUES (1, 3, 5);
Result:
+---------------+-----------------+---------------+
| suggestion_id | num_of_comments | sum_of_values |
+---------------+-----------------+---------------+
| 1 | 2 | 9 |
| 2 | 3 | 6 |
| 3 | 1 | 5 |
+---------------+-----------------+---------------+
3 rows in set (0.00 sec)
UPDATE: #Naktibalda's solution is an alternative solution that avoids sub queries.
I was typing the same query as potatopeelings.
But there is an issue:
Resultset after joins contains M*N rows (M-number of comments, N-number of votes, not less than 1) for each suggestion.
To avoid that you have to count distinct comment ids and divide a sum of votes by number of comments.
SELECT
s.*,
COUNT(DISTINCT c.scommentid) AS comment_count,
SUM(v.value)/GREATEST(COUNT(DISTINCT c.scommentid), 1) AS total_votes
FROM suggestions AS s
LEFT JOIN suggestion_comments AS c ON s.suggestion_id = c.suggestion_id
LEFT JOIN suggestion_votes AS v ON s.suggestion_id = v.suggestion_id
GROUP BY s.suggestion_id
ORDER BY total_votes DESC
LIMIT 30