Write SQL query for the given scenario - sql

We have a table without date or incremental int column as below.
create table test(
id uniqueidentifier DEFAULT newsequentialid(),
userid int,
type int,
value varchar(20)
);
We wanted userid with value based on following logic, first data in case of primary else last data in case of other type.
Available type column value
-------------
0 - None
1 - Primary
2 - Other
3 - Registered
Logic to retrieve value should be as below :
if Primary available :
take "first" Primary's Value
else if Registered available :
take "last" Registered's Value
else if Other available :
take "last" Other's Value
else
take "last" None's Value
Created few scenarios to understand requirement better.
Scenario 1 :
/*
Output should be as below
userid, value
-------------
1, User1 value1.1
2, User2 value4.2
*/
insert into test (userid, type, value) values(1, 1, 'User1 value1.1')
insert into test (userid, type, value) values(1, 1, 'User1 value1.2')
insert into test (userid, type, value) values(1, 2, 'User1 value2.1')
insert into test (userid, type, value) values(1, 2, 'User1 value2.2')
insert into test (userid, type, value) values(1, 4, 'User1 value4.1')
insert into test (userid, type, value) values(1, 4, 'User1 value4.2')
insert into test (userid, type, value) values(2, 0, 'User2 value0.1')
insert into test (userid, type, value) values(2, 0, 'User2 value0.2')
insert into test (userid, type, value) values(2, 4, 'User2 value4.1')
insert into test (userid, type, value) values(2, 4, 'User2 value4.2')
Scenario 2 :
/*
Output should be as below
userid, value
-------------
3, User3 value1.1
4, User4 value4.2
*/
insert into test (userid, type, value) values(3, 0, 'User3 value0.1')
insert into test (userid, type, value) values(3, 1, 'User3 value1.1')
insert into test (userid, type, value) values(3, 2, 'User3 value2.1')
insert into test (userid, type, value) values(3, 2, 'User3 value2.2')
insert into test (userid, type, value) values(4, 4, 'User4 value4.1')
insert into test (userid, type, value) values(4, 4, 'User4 value4.2')
insert into test (userid, type, value) values(4, 2, 'User4 value2.1')
insert into test (userid, type, value) values(4, 0, 'User4 value0.1')
Scenario 3 :
/*
Output should be as below
userid, value
-------------
5, User5 value2.1
6, User6 value0.3
*/
insert into test (userid, type, value) values(5, 2, 'User1 value1.1')
insert into test (userid, type, value) values(5, 2, 'User1 value2.1')
insert into test (userid, type, value) values(5, 0, 'User1 value0.1')
insert into test (userid, type, value) values(6, 0, 'User6 value0.1')
insert into test (userid, type, value) values(6, 0, 'User6 value0.2')
insert into test (userid, type, value) values(6, 0, 'User6 value0.3')
Highly appreciate any help.

Is this what you want?
select t.*
from (select t.*,
row_number() over (partition by user_id
order by (case when type = 1 then 1 when type = 3 then 2 when type = 2 then 3 else 4 end),
(case when type = 1 then id end) asc,
id desc
) as seqnum
from test t
) t
where seqnum = 1;

Related

SQL search full time or partial time

I am trying to create a stored procedure that can filter on a full or partial time. It should only filter on hours or minutes (not seconds) or both hours and minutes.
Using the sample data below:
#StartTimeFilter = '09:15' --> should return record #1
#StartTimeFilter = '10' --> should return records 2, 3, 10
#StartTimeFilter = '5' --> should return records 1, 2, 5, 6, 7, 8, 9
#StartTimeFilter = '45' --> should return records 5, 6
#StartTimeFilter = '13:45' --> should return record #6
#StartTimeFilter = '11:' --> should return records 4, 5
Code:
CREATE TABLE test
(
id INT,
startTime DateTime
);
INSERT INTO test (id, startTime) VALUES (1, '2021-10-25 09:15:00');
INSERT INTO test (id, startTime) VALUES (2, '2021-10-25 10:15:00');
INSERT INTO test (id, startTime) VALUES (3, '2021-10-25 10:30:00');
INSERT INTO test (id, startTime) VALUES (4, '2021-10-25 11:30:00');
INSERT INTO test (id, startTime) VALUES (5, '2021-10-25 11:45:00');
INSERT INTO test (id, startTime) VALUES (6, '2021-10-25 13:45:00');
INSERT INTO test (id, startTime) VALUES (7, '2021-10-25 14:50:00');
INSERT INTO test (id, startTime) VALUES (8, '2021-10-25 15:51:00');
INSERT INTO test (id, startTime) VALUES (9, '2021-10-25 15:58:00');
INSERT INTO test (id, startTime) VALUES (10,'2021-10-25 16:10:00');
It looks like you need a simple like string comparision:
declare #StartTimeFilter varchar(10)='5'
select *
from test
where Convert(varchar(5),starttime,114) like Concat('%',#StartTimeFilter,'%')

MS SQL Server. Transpose columns of data to rows of data

I have a Details table which can be described by the following:
Create table #Details (
Id int identity,
HeaderId int,
ChannelId int,
Value float
)
We insert some seed data:
insert into #Details (HeaderId, ChannelId, Value) values(1, 0, 1019.51)
insert into #Details (HeaderId, ChannelId, Value) values(1, 1, 20.1)
insert into #Details (HeaderId, ChannelId, Value) values(1, 2, 21.2)
insert into #Details (HeaderId, ChannelId, Value) values(1, 3, 22.3)
insert into #Details (HeaderId, ChannelId, Value) values(1, 4, 23.4)
insert into #Details (HeaderId, ChannelId, Value) values(2, 0, 1020.62)
insert into #Details (HeaderId, ChannelId, Value) values(2, 1, 26.1)
insert into #Details (HeaderId, ChannelId, Value) values(2, 2, 27.2)
insert into #Details (HeaderId, ChannelId, Value) values(2, 3, 28.3)
insert into #Details (HeaderId, ChannelId, Value) values(2, 4, 29.4)
Select All produces the following:
I would like to transpose the column data to rows, to produce the output below:
Thanks in advance.
Here you go. Just select first HeaderId,value, ChannelId in a subquery and then pivot.
DECLARE #tbl TABLE (Id int identity,
HeaderId int,
ChannelId int,
Value float)
insert into #tbl (HeaderId, ChannelId, Value) values(1, 0, 1019.51)
insert into #tbl (HeaderId, ChannelId, Value) values(1, 1, 20.1)
insert into #tbl (HeaderId, ChannelId, Value) values(1, 2, 21.2)
insert into #tbl (HeaderId, ChannelId, Value) values(1, 3, 22.3)
insert into #tbl (HeaderId, ChannelId, Value) values(1, 4, 23.4)
insert into #tbl (HeaderId, ChannelId, Value) values(2, 0, 1020.62)
insert into #tbl (HeaderId, ChannelId, Value) values(2, 1, 26.1)
insert into #tbl (HeaderId, ChannelId, Value) values(2, 2, 27.2)
insert into #tbl (HeaderId, ChannelId, Value) values(2, 3, 28.3)
insert into #tbl (HeaderId, ChannelId, Value) values(2, 4, 29.4)
SELECT *
FROM
(
SELECT HeaderId,value, ChannelId
FROM
#tbl
)T
PIVOT
(
SUM(value)
FOR ChannelId IN ([0],[1], [2], [3], [4]
)
) AS PivotTable;
Result below:
This should work:
select HeaderId
, [0] as ChannelId0
, [1] as ChannelId1
, [2] as ChannelId2
, [3] as ChannelId3
, [4] as ChannelId4
from
(
select HeaderId, ChannelId,Value
from #Details
) x
pivot
(
max(Value)
for ChannelId in([0],[1],[2], [3],[4],[5])
)p

SQL query for most matching tags

Let's imagine abstract database, which contains objects with attached tags (default many-to-many relation with additional table). Now, we have a user, who wants to find objects matching the most of tags he provides, sorted by relevance. For example:
Object1: TagA, TagB, TagC, TagD
Object2: TagA, TagC, TagD
Object3: TagB, TagD, TagE, TagF
Object4: TagA, TagB, TagC, TagE
User asks for TagB, TagC, TagD
Expected result should look like following:
Object1 (3 matches)
Object4 (2 matches)
Object2 (2 matches)
Object3 (1 match)
Edit: Sample structure (I created it in SQLite, but we may safely assume MSSql)
CREATE TABLE Objects(Id int not null primary key, Name text);
CREATE TABLE Tags(Id int not null primary key, Name text);
CREATE TABLE ObjectTag(Id int not null primary key, ObjectId int, TagId int);
INSERT INTO Objects(Id, Name) VALUES (1, "Object1");
INSERT INTO Objects(Id, Name) VALUES (2, "Object2");
INSERT INTO Objects(Id, Name) VALUES (3, "Object3");
INSERT INTO Objects(Id, Name) VALUES (4, "Object4");
INSERT INTO Tags(Id, Name) VALUES (1, "TagA");
INSERT INTO Tags(Id, Name) VALUES (2, "TagB");
INSERT INTO Tags(Id, Name) VALUES (3, "TagC");
INSERT INTO Tags(Id, Name) VALUES (4, "TagD");
INSERT INTO Tags(Id, Name) VALUES (5, "TagE");
INSERT INTO Tags(Id, Name) VALUES (6, "TagF");
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (1, 1, 1);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (2, 1, 2);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (3, 1, 3);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (4, 1, 4);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (5, 2, 1);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (6, 2, 3);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (7, 2, 4);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (8, 3, 2);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (9, 3, 4);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (10, 3, 5);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (11, 3, 6);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (12, 4, 1);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (13, 4, 2);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (14, 4, 3);
INSERT INTO ObjectTag(Id, ObjectId, TagId) VALUES (15, 4, 5);
Expected result (for query "TagB, TagC, TagD"):
ObjectName | MatchCount
-----------+-----------
Object1 | 3
Object4 | 2
Object2 | 2
Object3 | 1
Not sure about your table structure but from your post looks like you can use GROUP BY to achieve this like
select object, count(tag) as tagcount
from tags
where tag in ('TagB', 'TagC', 'TagD')
group by object;
Here's an answer:
SELECT t1.name, COUNT(t1.tag) as count FROM
(SELECT o.Name as name,
1 as tag
FROM Objects AS o,
ObjectTag AS ot
WHERE o.Id = ot.ObjectId
AND ot.TagId IN (2,3,4)) AS t1
GROUP BY t1.name
ORDER BY count DESC

How to insert multiple row values into 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.

Update / Merge table with summing values

I have a schema:
http://sqlfiddle.com/#!4/e9917/1
CREATE TABLE test_table (
id NUMBER,
period NUMBER,
amount NUMBER
);
INSERT INTO test_table VALUES (1000, 1, 100);
INSERT INTO test_table VALUES (1000, 1, 500);
INSERT INTO test_table VALUES (1001, 1, 200);
INSERT INTO test_table VALUES (1001, 2, 300);
INSERT INTO test_table VALUES (1002, 1, 900);
INSERT INTO test_table VALUES (1002, 1, 250);
I want to update the amount field by adding amounts of records which has same (id, period) pair. like after op :
ID| period| amount
1000 1 600
1001 1 200
1001 2 300
1002 1 1150
I Couldn't figure out how :(
EDIT:
In actual case this table is populated by insertion operation from other 2 tables:
CREATE TABLE some_table1(
id NUMBER,
period NUMBER,
amount NUMBER
);
INSERT INTO some_table1 VALUES (1000, 1, 100);
INSERT INTO some_table1 VALUES (1000, 1, 500);
INSERT INTO some_table1 VALUES (1001, 1, 200);
INSERT INTO some_table1 VALUES (1001, 2, 300);
INSERT INTO some_table1 VALUES (1002, 1, 900);
INSERT INTO some_table1 VALUES (1002, 1, 250);
CREATE TABLE some_table2(
id NUMBER,
period NUMBER,
amount NUMBER
);
INSERT INTO some_table2 VALUES (1000, 1, 30);
INSERT INTO some_table2 VALUES (1000, 1, 20);
INSERT INTO some_table2 VALUES (1001, 1, 15);
INSERT INTO some_table2 VALUES (1001, 2, 20);
INSERT INTO some_table2 VALUES (1002, 1, 50);
INSERT INTO some_table2 VALUES (1002, 1, 60);
Dublicates occures when two insertions done:
INSERT INTO TEST_TABLE (id,period,amount) SELECT id,period,amount from some_table1
INSERT INTO TEST_TABLE (id,period,amount) SELECT id,period,amount from some_table2
new sqlfiddle link: http://sqlfiddle.com/#!4/cd45b/1
May be it can be solved during insertion from two table..
A script like this would do what you want:
CREATE TABLE test_table_summary (
id NUMBER,
period NUMBER,
amount NUMBER
);
INSERT INTO test_table_summary (id, period, amount)
SELECT id, period, SUM(amount) AS total_amount FROM test_table
GROUP BY id, period;
DELETE FROM test_table;
INSERT INTO test_table (id, period, amount)
SELECT id, period, total_amount FROM test_table_summary;
DROP TABLE test_table_summary;
But you should actually decide if test_table is to have a primary key and the total amount or all the detail data. It's not a good solution to use one table for both.
By what you have added, then I'd say you can use the Oracle MERGE INTO statement:
MERGE INTO test_table t
USING (SELECT id, period, amount FROM some_table1) s
ON (t.id=s.id AND t.period=s.period)
WHEN MATCHED THEN UPDATE SET t.amount=t.amount+s.amount
WHEN NOT MATCHED THEN INSERT (t.id, t.period, t.amount)
VALUES (s.id, s.period, s.amount);
Beware though... this will work only if test_table already has no duplicate id, period rows to begin with. So if your table is already messed up, you still have to reinitialize it properly a first time (and maybe add a unique id, period key to avoid problems in the future).