Displaying SQL SELECT rows in a table display - sql-server

My database structure and sample data:
CREATE TABLE [dbo].[users] (
[user_id] [bigint] IDENTITY(1,1) NOT NULL,
[user_name] [nvarchar](50) NULL,
[first_name] [nvarchar](50) NULL,
[last_name] [nvarchar](50) NULL,
[id_number] [nvarchar](50) NULL,
CONSTRAINT [PK_users] PRIMARY KEY CLUSTERED
(
[user_id] ASC
)
)
insert into users (user_name, first_name, last_name, id_number)
select 'user1','John','Brown',7707071231
union all
select 'user2','Mary','Jane',7303034432
union all
select 'user3','Peter','Pan',5503024441
CREATE TABLE [dbo].[quiz_results] (
[result_id] [bigint] IDENTITY(1,1) NOT NULL,
[quiz_id] [bigint] NOT NULL,
[user_id] [bigint] NOT NULL,
[grade] [bigint] NULL,
CONSTRAINT [PK_quizresults] PRIMARY KEY CLUSTERED
(
[result_id] ASC
)
)
insert into quiz_results (quiz_id, user_id, grade)
select 1,1,88
union all
select 2,1,84
union all
select 3,1,33
union all
select 1,2,65
This query gives me the quiz results for user_id = 1:
SELECT
users.first_name + ' ' + users.last_name + ' (' + users.id_number + ')' AS student_name,
quiz.quiz_name,
quiz_results.grade
FROM quiz_results
INNER JOIN quiz ON quiz_results.quiz_id = quiz.quiz_id
INNER JOIN users ON quiz_results.user_id = users.user_id
WHERE users.user_id = 12345
like this:
+-------------------------+-----------+-------+
| student_name | quiz_name | grade |
+-------------------------+-----------+-------+
| John Brown (7707071231) | quiz a | 88 |
| John Brown (7707071231) | quiz b | 84 |
| John Brown (7707071231) | quiz c | 33 |
+-------------------------+-----------+-------+
But I don't want the student_name shown on each row. I want this output:
+-------------------------+
| John Brown (7707071231) |
+-------------------------+
| quiz a | 88 |
| quiz b | 84 |
| quiz c | 33 |
+-------------------------+
The student_name is on the first row followed by one row for each quiz result - I specifically want the student_name on the first row.
The query will only ever be for one student_name. Essentially, I want to produce a "certificate" directly in the SQL.
What is the best SQL way to get the data into that format? Will a CTE or the STUFF() command work? Or is there a better way?

This simply can't be done in SQL.
SQL can only return scalar values or result sets (tabular data), and result sets don't support "column span" - so the only way to do it is in the presentation layer - but you can do some things in SQL Server to make your job in the presentation layer easier.
One option is to create a stored procedure that will return the student name as an output parameter, and the quiz grades as a result set:
CREATE PROCEDURE GetQuizResultByUserId
(
#UserId int,
#UserName nvarchar(154) OUTPUT
)
AS
-- it's 154 because 50 + 1 + 50 + 2 + 50 + 1
SELECT #UserName = first_name + ' ' + last_name + ' (' + id_number + ')'
FROM users
WHERE user_id = #UserId
SELECT
quiz.quiz_name,
quiz_results.grade
FROM quiz_results
INNER JOIN quiz ON quiz_results.quiz_id = quiz.quiz_id
WHERE quiz_results.user_id = #UserId
GO
Another option, since this is 2016 version, is to return the results as Json, using the For Json clause:
SELECT first_name + ' ' + last_name + ' (' + id_number + ')' As UserName,
(
SELECT quiz.quiz_name,
quiz_results.grade
FROM quiz_results
INNER JOIN quiz ON quiz_results.quiz_id = quiz.quiz_id
WHERE quiz_results.user_id = #UserId
FOR JSON AUTO
) As quizResult
FROM users
WHERE user_id = #UserId
FOR JSON AUTO
The result is the following json:
[
{
"UserName": "John Brown (7707071231)",
"quizResult": [
{
"quiz_name": "quiz a",
"grade": 88
},
{
"quiz_name": "quiz b",
"grade": 84
},
{
"quiz_name": "quiz c",
"grade": 33
}
]
}
]

Related

How to multi-select filter an EAV table in SQL Server

I have an EAV table with attributes and would like to do a hybrid selection of the items based on variables that are passed into a stored procedure.
Sample table:
| group_id | item_id | key | value |
+----------+---------+--------+-------+
| 1 | AA | length | 10 |
| 1 | AA | width | 10 |
| 1 | AA | color | white |
| 1 | AA | brand | beta |
| 1 | BB | length | 25 |
| 1 | BB | brand | alpha |
| 2 | CC | brand | alpha |
Sample query:
declare #attributes nvarchar(max) = 'brand name, length'
declare #attributeValues nvarchar(max) = 'alpha, beta, 25'
declare #id int = 1
select *
into #allProductsFromGroup
from items
where group_id = #id
select item_id
from #allProductsFromGroup #all
where [key] in (select value from string_split(#attributes, ','))
and [value] in (select value from string_split(#attributeValues, ','))
Expected output:
| item_id |
+---------+
| BB |
I could hard-code in and and or statements for each key, but there are many, and I am looking for a more scalable solution.
Passing in and parsing JSON would be good, like:
[
{ "brand": "aplha" },
{ "brand": "beta" },
{ "length": 25 }
]
How can I write the second select to dynamically return a subset of allProductsFromGroup that dynamically include multiple results from the same group (multi-select brand or multi-select length), but exclude from other groups (color, length, etc.)?
The target query might look something like this:
with q as
(
select item_id,
max( case when [key] = 'brand' then [value] end ) brand,
max( case when [key] = 'length' then cast( [value] as int ) end ) length,
from #allProductsFromGroup
group by Item_id
)
select item_id
from q
where brand in ('alpha','beta') and length=25
You just have to build it from the incoming data (yuck). A simpler query form to generate might be something like
select item_id
from #allProductsFromGroup
where [key] = 'brand' and [value] in ('alpha','beta')
intersect
select item_id
from #allProductsFromGroup
where [key] = 'length' and [value] = 25
mapping and criteria to intersect, and or criteria to union. It's likely to be cheaper too, as each query can seek an index on (key,value).
It's probably a late answer, but if you can pass conditions as JSON, the next approach is also a possible solution. The JSON must be in the same format as in the answer and you may use more than two conditions:
Table:
CREATE TABLE Data (
group_id int,
item_id varchar(2),
[key] varchar(100),
[value] varchar(100)
)
INSERT INTO Data (group_id, item_id, [key], [value])
VALUES
(1, 'AA', 'length', '10'),
(1, 'AA', 'width', '10'),
(1, 'AA', 'color', 'white'),
(1, 'AA', 'brand', 'beta'),
(1, 'BB', 'length', '25'),
(1, 'BB', 'brand', 'alpha'),
(2, 'CC', 'brand', 'alpha')
Conditions as JSON:
DECLARE #conditions varchar(max) = N'
[
{"key": "brand", "values": ["alpha", "beta"]},
{"key": "length", "values": ["25"]}
]
'
Statement:
SELECT d.item_id
FROM Data d
JOIN (
SELECT j1.[key], j2.[value]
FROM OPENJSON(#conditions) WITH (
[key] varchar(100) '$.key',
[values] nvarchar(max) '$.values' AS JSON
) j1
CROSS APPLY OPENJSON(j1.[values]) j2
) o ON d.[key] = o.[key] AND d.[value] = o.[value]
GROUP BY d.item_id
HAVING COUNT(*) = (SELECT COUNT(*) FROM OPENJSON(#conditions))
Result:
item_id
BB

Get all rows between start and end flag

I've got a similar data structure
Parameter | Value | DateTime
----------------------------
Switch | "on" | 2019-10-13 15:01:25
Temp | 25 | 2019-10-13 15:01:37
Pressure | 1006 | 2019-10-13 15:01:53
...
Temp | 22 | 2019-10-13 15:04:41
Switch | "off" | 2019-10-13 15:04:59
...
Switch | "on" | 2019-10-13 17:14:51
Temp | 27 | 2019-10-13 17:15:07
...
Switch | "off" | 2019-10-13 17:17:43
Between each pair of Switch "on" and "off" I have to calculate the values for the parameters, i.e. average or max/min and so on. How can I get the different data sets to have multiple groups for the calculation?
I think this should be solvable with
- Stored Procedure (statement?)
- SSIS package (how?)
- .NET application.
What might be the best way to solve this issue?
Thanks in advance.
Update
This is the full structure of the table.
CREATE TABLE [schema].[foo]
(
[Id] UNIQUEIDENTIFIER NOT NULL PRIMARY KEY,
[Group] VARCHAR(20) NOT NULL,
[Parameter] VARCHAR(50) NOT NULL,
[Type] VARCHAR(50) NOT NULL,
[Timestamp] DATETIME NOT NULL,
[Value] NVARCHAR(255) NOT NULL,
[Unit] VARCHAR(10) NOT NULL,
// Only for logging. No logic for the use case.
[InsertedTimestampUtc] DATETIME NOT NULL DEFAULT(GetUtcDate()),
[IsProcessed] INT NOT NULL DEFAULT(0)
)
If I understand your question correctly, the next approach may help to get the expected results:
Table:
CREATE TABLE #Data (
[DateTime] datetime,
[Parameter] varchar(50),
[Value] varchar(10)
)
INSERT INTO #Data
([DateTime], [Parameter], [Value])
VALUES
('2019-10-13T15:01:25', 'Switch', 'on'),
('2019-10-13T15:01:37', 'Temp', '25'),
('2019-10-13T15:01:53', 'Pressure', '1006'),
('2019-10-13T15:04:41', 'Temp', '22'),
('2019-10-13T15:04:59', 'Switch', 'off'),
('2019-10-13T17:14:51', 'Switch', 'on'),
('2019-10-13T17:15:07', 'Temp', '27'),
('2019-10-13T17:17:43', 'Switch', 'off')
Statement:
;WITH ChangesCTE AS (
SELECT
*,
CASE WHEN [Parameter] = 'Switch' AND [Value] = 'on' THEN 1 ELSE 0 END AS ChangeIndex
FROM #Data
), GroupsCTE AS (
SELECT
*,
SUM(ChangeIndex) OVER (ORDER BY [DateTime]) AS GroupIndex
FROM ChangesCTE
)
SELECT [GroupIndex], [Parameter], AVG(TRY_CONVERT(int, [Value]) * 1.0) AS [AvgValue]
FROM GroupsCTE
WHERE [Parameter] <> 'Switch'
GROUP BY [GroupIndex], [Parameter]
Results:
GroupIndex Parameter AvgValue
1 Pressure 1006.000000
1 Temp 23.500000
2 Temp 27.000000

Splitting a string and inserting the parts in the correct columns inside a table

I am trying to build a fact table with sql server. It currently looks like this:
[Item] [Variant Descr.] [Variant Order] [Dim_Colour] [Dim_Size] [Dim_Style]
----------------------------------------------------------------------------
01 NAVY/44 COLOUR/SIZE NULL NULL NULL
02 BLACK/S4 COLOUR/STYLE NULL NULL NULL
I need to split the String in [Variant Descr.] and insert the parts into the correct Dim_ Column so the table will eventually look like this:
[Item] [Variant Descr.] [Variant Order] [Dim_Colour] [Dim_Size] [Dim_Style]
----------------------------------------------------------------------------
01 NAVY/44 COLOUR/SIZE NAVY 44 NULL
02 BLACK/S4 COLOUR/STYLE BLACK NULL S4
The problem is that the parts of [Variant Order] and the number of parts of [Variant Descr.] may vary for each row. So I basically need to do something like:
Get the String before the '/' in [Variant Descr.]
Get the String before the '/' in [Variant Order]
Insert the first value into the column specified by the second value
Do this for all parts of [Variant Descr.]
Do this for every row in the table
I have already tried to solve this with a user-defined function, only to find out that I cannot use dynamic SQL within a UDF.
Any help would be greatly appreciated
Cheers!
One possible approach is to split the texts in [Variant Descr.] and [Variant Order] columns and update the table with dynamic statement.
Although using STRING_SPLIT() is the first choice starting with SQL Server 2016, this function is not an option in this case, because the order of the substrings is not guaranteed.
A working solution is to use OPENJSON() - columns values are transformed into a valid JSON object ('NAVY/44' is translated into '["NAVY", "44"]' for example) and substrings are retrieved using OPENJSON().
Input:
CREATE TABLE #Data (
[Item] varchar(10),
[Variant Descr.] varchar(50),
[Variant Order] varchar(50),
[Dim_Colour] varchar(50),
[Dim_Size] varchar(50),
[Dim_Style] varchar(50)
)
INSERT INTO #Data
([Item], [Variant Descr.], [Variant Order], [Dim_Colour], [Dim_Size], [Dim_Style])
VALUES
('01', 'NAVY/44', 'COLOUR/SIZE', NULL, NULL, NULL),
('02', 'BLACK/S4', 'COLOUR/STYLE', NULL, NULL, NULL),
('03', 'NAVY/44/S4', 'COLOUR/SIZE/STYLE', NULL, NULL, NULL),
('04', 'GREEN', 'COLOUR', NULL, NULL, NULL)
T-SQL:
-- Dynamic statement
DECLARE #stm nvarchar(max) = N''
SELECT #stm = #stm +
N'UPDATE #Data ' +
N'SET ' +
QUOTENAME('Dim_' + j1.[value]) +
N' = ''' +
j2.[value] +
N''' WHERE Item = ''' +
d.Item +
N'''; '
FROM #Data d
CROSS APPLY OPENJSON(CONCAT('["', REPLACE([Variant Order], '/', '","'), '"]')) j1
CROSS APPLY OPENJSON(CONCAT('["', REPLACE([Variant Descr.], '/', '","'), '"]')) j2
WHERE j1.[key] = j2.[key]
-- Execution and output
EXEC (#stm)
SELECT *
FROM #Data
Output:
-----------------------------------------------------------------------------
Item Variant Descr. Variant Order Dim_Colour Dim_Size Dim_Style
-----------------------------------------------------------------------------
01 NAVY/44 COLOUR/SIZE NAVY 44
02 BLACK/S4 COLOUR/STYLE BLACK S4
03 NAVY/44/S4 COLOUR/SIZE/STYLE NAVY 44 S4
04 GREEN COLOUR GREEN
You can get required output by -
select [item] ,
[variant descr],[variant order],
left([variant descr],charindex('/', [variant descr]) - 1) AS [Dim_Colour],
CASE WHEN [variant order] like '%/SIZE' THEN SUBSTRING([variant descr], CHARINDEX('/', [variant descr]) +1, 100)
ELSE '' END AS [Dim_Size],
CASE WHEN [variant order] like '%/STYLE' THEN SUBSTRING([variant descr], CHARINDEX('/', [variant descr]) +1, 100)
ELSE '' END AS [Dim_Style]
from your_table_name
Try This
IF OBJECT_ID('tempdb..#temp')IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp (
[Item] INT,
[Variant Descr.] VARCHAR(100),
[Variant Order] VARCHAR(100) ,
[Dim_Colour] VARCHAR(100),
[Dim_Size]INT ,
[Dim_Style] VARCHAR(100)
)
INSERT INTO #temp
SELECT 01,'NAVY/44' ,'COLOUR/SIZE' ,NULL,NULL,NULL UNION ALL
SELECT 02,'BLACK/S4','COLOUR/STYLE',NULL,NULL,NULL
SELECT * FROM #temp
UPDATE o
SET o.[Dim_Colour] = dt.Dim_Colour,
o.Dim_Size = dt.Dim_Size,
o.Dim_Style = dt.Dim_Style
FROM #temp o
INNER JOIN
(
SELECT [Item], [Variant Descr.], [Variant Order] ,
SUBSTRING([Variant Descr.],0,CHARINDEX('/',[Variant Descr.])) AS [Dim_Colour]
,CASE WHEN ISNUMERIC(SUBSTRING([Variant Descr.],CHARINDEX('/',[Variant Descr.])+1,LEN ([Variant Descr.]))) = 1 AND [Variant Order] = 'COLOUR/SIZE'
THEN SUBSTRING([Variant Descr.],CHARINDEX('/',[Variant Descr.])+1,LEN ([Variant Descr.]))
ELSE NULL END AS [Dim_Size]
,CASE WHEN ISNUMERIC(SUBSTRING([Variant Descr.],CHARINDEX('/',[Variant Descr.])+1,LEN ([Variant Descr.]))) <> 1 AND [Variant Order] ='COLOUR/STYLE'
THEN SUBSTRING([Variant Descr.],CHARINDEX('/',[Variant Descr.])+1,LEN ([Variant Descr.]))
ELSE NULL END AS [Dim_Style]
FROM #temp i
)dt ON dt.[Item] = o.[Item]
SELECT * FROM #temp
Result
Item Variant Descr. Variant Order Dim_Colour Dim_Size Dim_Style
------------------------------------------------------------------------------
1 NAVY/44 COLOUR/SIZE NAVY 44 NULL
2 BLACK/S4 COLOUR/STYLE BLACK NULL S4
And here is a fully generic approach to split this up (thx Zhorov for the MCVE!)
CREATE TABLE #Data (
[Item] varchar(10),
[Variant Descr.] varchar(50),
[Variant Order] varchar(50),
[Dim_Colour] varchar(50),
[Dim_Size] varchar(50),
[Dim_Style] varchar(50)
)
INSERT INTO #Data
([Item], [Variant Descr.], [Variant Order], [Dim_Colour], [Dim_Size], [Dim_Style])
VALUES
('01', 'NAVY/44', 'COLOUR/SIZE', NULL, NULL, NULL),
('02', 'BLACK/S4', 'COLOUR/STYLE', NULL, NULL, NULL),
('03', 'NAVY/44/S4', 'COLOUR/SIZE/STYLE', NULL, NULL, NULL),
('04', 'GREEN/1/2/3/4', 'COLOUR/aNewOne/SIZE/EvenMore/STYLE', NULL, NULL, NULL);
GO
WITH Casted AS
(
SELECT *
,CAST('<x>' + REPLACE(d.[Variant Order],'/','</x><x>') + '</x>' AS XML) AS OrderXml
,CAST('<x>' + REPLACE(d.[Variant Descr.],'/','</x><x>') + '</x>' AS XML) AS DescrXml
FROM #Data d
)
SELECT c.Item
,A.Position
,c.OrderXml.value('/x[sql:column("Position")][1]','nvarchar(max)') AS OrderKey
,c.DescrXml.value('/x[sql:column("Position")][1]','nvarchar(max)') AS DescrValue
FROM Casted c
CROSS APPLY(SELECT TOP(SELECT c.OrderXml.value('count(/*)','int')) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) FROM master..spt_values) A(Position)
GO
DROP TABLE #Data;
The result
+------+----------+----------+------------+
| Item | Position | OrderKey | DescrValue |
+------+----------+----------+------------+
| 02 | 1 | COLOUR | BLACK |
+------+----------+----------+------------+
| 02 | 2 | STYLE | S4 |
+------+----------+----------+------------+
| 04 | 1 | COLOUR | GREEN |
+------+----------+----------+------------+
| 04 | 2 | aNewOne | 1 |
+------+----------+----------+------------+
| 04 | 3 | SIZE | 2 |
+------+----------+----------+------------+
| 04 | 4 | EvenMore | 3 |
+------+----------+----------+------------+
| 04 | 5 | STYLE | 4 |
+------+----------+----------+------------+
| 01 | 1 | COLOUR | NAVY |
+------+----------+----------+------------+
| 01 | 2 | SIZE | 44 |
+------+----------+----------+------------+
| 03 | 1 | COLOUR | NAVY |
+------+----------+----------+------------+
| 03 | 2 | SIZE | 44 |
+------+----------+----------+------------+
| 03 | 3 | STYLE | S4 |
+------+----------+----------+------------+
You can proceed with conditional aggregation or with a PIVOT approach.
The idea in short:
I use a cast to XML to allow to reach the fragment by its position.
Furthermore I use CROSS APPLY together with ROW_NUMBER, which will return a list of numbers depending on the count of the current row's list of fragments. This number is now used within sql:column() to read the fitting fragments side-by-side.

SQL Server 2016 - Inserting remaining rows into a table leading to duplicates of existing rows

I have 4 tables: People Status, People, Codes and PeopleStatusCodes with the following schemas:
People:
[ID] INT IDENTITY (1, 1) CONSTRAINT [PK_People_ID] PRIMARY KEY,
[PersonCode] VARCHAR(MAX) NOT NULL,
[FirstName] VARCHAR(MAX) NOT NULL,
[LastName] VARCHAR(MAX) NOT NULL
PeopleStatus:
[ID] INT IDENTITY (1, 1) CONSTRAINT [PK_PeopleStatus_ID] PRIMARY KEY,
[PeopleID] VARCHAR(MAX) NOT NULL FOREIGN KEY REFERENCES [People]([ID]),
[Status] INT NOT NULL
Codes:
[ID] INT IDENTITY (1, 1) CONSTRAINT [PK_Codes_ID] PRIMARY KEY,
[CodeNumber] VARCHAR(MAX) NOT NULL,
[Name] VARCHAR(MAX) NOT NULL
PeopleStatusCodes:
[ID] INT IDENTITY (1, 1) CONSTRAINT [PK_PeopleStatusCodes_ID] PRIMARY KEY,
[PeopleStatusID] INT NOT NULL FOREIGN KEY REFERENCES [PeopleStatus]([ID]),
[CodeID] INT NOT NULL FOREIGN KEY REFERENCES [Codes]([ID]),
[Result] INT NOT NULL, --success = 1, fail=0
I am attempting to insert 3 rows of data into the PeopleStatusCodes table - 1 row where the Result = 1, and the remaining rows where Result = 0.
The code below declares 2 temporary tables - one to store the Person's PeopleStatus ID (#peopleStatus) the other to store the data (#data). It then checks that the Person does not already have an entry in the PeopleStatus table - if it does not, a new entry in the PeopleStatus table is created, and that ID is inserted into #peopleStatus. If an entry already exists, the ID of that entry is inserted into #peopleStatus.
An entry is then inserted into PeopleStatusCodes table based off #data, with Result = 1. After that, entries for the remaining Codes which do not have matching data are inserted with Result = 0.
--declare temporary tables
DECLARE #peopleStatus TABLE (peopleStatusID INT)
DECLARE #data TABLE (FirstName VARCHAR (100), LastName VARCHAR (100), Codename VARCHAR (100))
--insert data into #data
INSERT INTO #data(
[FirstName]
,[LastName]
,[Codename]
)
VALUES(
'John'
,'Smith'
,'02 - Code2'
)
--check if entry exists inside PeopleStatus and insert into #peopleStatus based on that
IF NOT EXISTS (SELECT [ps].[PersonCode] FROM PeopleStatus [ps], People [p], #data [d]
WHERE [ps].[PersonCode] = [p].[PersonCode]
AND [p].[FirstName] = [d].[FirstName]
AND [p].[LastName] = [d].[LastName])
INSERT INTO PeopleStatus (
[PersonCode]
,[Status]
)
OUTPUT inserted.[ID]
INTO #peopleStatus
SELECT
[p].[PersonCode]
,1
FROM [People] [p], #data [d]
WHERE [p].[FirstName] = [d].[FirstName]
AND [p].[LastName] = [d].[LastName]
ELSE INSERT INTO #peopleStatus (peopleStatusID)
SELECT [ps].[ID]
FROM PeopleStatus [ps], People [p], #data [d]
WHERE [ps].[PersonCode] = [p].[PersonCode]
AND [p].[FirstName] = [d].[FirstName]
AND [p].[LastName] = [d].[LastName]
--insert into PeopleStatusCodes a row of data with Result = 1 based off data stored in #data
INSERT INTO [dbo].[PeopleStatusCodes] (
[PeopleStatusID]
,[CodeID]
,[Result]
)
SELECT
[temp].[peopleStatusID]
,(SELECT ID FROM Codes WHERE CodeNumber + ' - ' + Name = [d].[Codename])
,1
FROM #peopleStatus [temp], #data [d]
--for every remaining Code in the Codes table which did not have a match with the data, insert into PeopleStatusCodes a row of data with Result = 0
DECLARE #IDColumn INT
SELECT #IDColumn = MIN(c.ID)
FROM Codes [c], PeopleStatusCodes [psc], #peopleStatus [temp]
WHERE [psc].CodeID != [c].ID
AND [psc].PeopleStatusID = [temp].peopleStatusID
WHILE #IDColumn IS NOT NULL
BEGIN
INSERT INTO [dbo].[PeopleStatusCodes] (
[PeopleStatusID]
,[CodeID]
,[Result]
)
SELECT
[temp].peopleStatusID
,#IDColumn
,0
FROM #peopleStatus [temp]
SELECT #IDColumn = MIN(c.ID)
FROM Codes [c], PeopleStatusCodes [psc], #peopleStatus [temp]
WHERE [psc].CodeID != [c].ID
AND [psc].PeopleStatusID = [temp].peopleStatusID
AND c.ID > #IDColumn
END
My problem is that when I run the code, instead of 3 entries in the PeopleStatusCodes table, I get 4 entries, with 1 entry a duplicate.
What I get:
+----+----------------+--------+--------+
| ID | PeopleStatusID | CodeID | Result |
+----+----------------+--------+--------+
| 1 | 1 | 2 | 1 |
| 2 | 1 | 1 | 0 |
| 3 | 1 | 2 | 0 |
| 4 | 1 | 3 | 0 |
+----+----------------+--------+--------+
What I want:
+----+----------------+--------+--------+
| ID | PeopleStatusID | CodeID | Result |
+----+----------------+--------+--------+
| 1 | 1 | 2 | 1 |
| 2 | 1 | 1 | 0 |
| 3 | 1 | 3 | 0 |
+----+----------------+--------+--------+
Update: I managed to solve it by going about it in a more straight forward way - insert all rows first, then update rows where necessary.
In the last pasrt, you could use a row number to remove duplicates:
;WITH ROW AS (
SELECT #IDColumn = MIN(c.ID),
ROW_NUMBER () OVER (PARTITION BY PeopleStatusID, CodeID ORDER BY
PeopleStatusID) AS ROW
FROM Codes [c], PeopleStatusCodes [psc], #peopleStatus [temp]
WHERE [psc].CodeID != [c].ID
AND [psc].PeopleStatusID = [temp].peopleStatusID
AND c.ID > #IDColumn )
SELECT * FROM ROW WHERE Row = 1
I managed to solve it by going about it a different way. Instead of inserting one row with Result = 1 followed by the remaining rows, I inserted ALL rows with default Result = 0. I then Updated the row that matched the data to have Result = 1.
--Inserts a row for every Code into PeopleStatusCodes
DECLARE #IDColumn VARCHAR (10)
SELECT #IDColumn = MIN(c.ID)
FROM Codes [c]
WHILE #IDColumn IS NOT NULL
BEGIN
INSERT INTO [dbo].[PeopleStatusCodes] (
[PeopleStatusID]
,[CodeID]
,[Result]
)
SELECT
[temp].[peopleStatusID]
,#IDColumn
,0
FROM #peopleStatus [temp]
SELECT #IDColumn = MIN(c.ID)
FROM Codes [c]
WHERE c.ID > #IDColumn
END
--Checks if the data matching row has not had Result changed to 1 already, and if so, update that row.
IF NOT EXISTS (SELECT [psc].ID
FROM PeopleStatusCodes [psc], #peopleStatus [temp]
WHERE [psc].PeopleStatusID = [temp].peopleStatusID
AND [psc].CodeID = (SELECT [c].ID FROM Codes [c], #data [d] WHERE [c].CodeNumber + ' - ' + [c].Name = [d].[Codename])
AND [psc].Result = 1)
UPDATE [dbo].[PeopleStatusCodes] SET Result = 1 WHERE CodeID = (SELECT [c].ID FROM Codes [c], #data [d] WHERE [c].CodeNumber + ' - ' + [c].Name = [d].[Codename])

Insert random Data content in SQL Server 2008

I know there are several topics on this, but none of them was suitable for me, that's why I took the chance to ask you again.
I have a table which has columns UserID, FirstName, Lastname.
I need to insert 300 000 records for each column and they have to be unique, for example:
UserID0001, John00001, Doe00001
UserID0002, John00002, Doe00002
UserID0003, John00003, Doe00003
I hope there is an easy way :)
Thank you in advance.
Best,
Lyubo
;with sequence as (
select N = row_number() over (order by ##spid)
from sys.all_columns c1, sys.all_columns c2
)
insert into [Table] (UserID, FirstName, Lastname)
select
'UserID' + right('000000' + cast(N as varchar(10)), 6),
'John' + right('000000' + cast(N as varchar(10)), 6),
'Doe' + right('000000' + cast(N as varchar(10)), 6)
from sequence where N <= 300000
You could use the ROW_NUMBER function to generate different numbers like this:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE dbo.users(
Id INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
user_id VARCHAR(20),
first_name VARCHAR(20),
last_name VARCHAR(20)
);
GO
DECLARE #NoOfRows INT = 7;
INSERT INTO dbo.users(user_id, first_name, last_name)
SELECT 'User_'+n, 'John_'+n, 'Doe_'+n
FROM(
SELECT REPLACE(STR(ROW_NUMBER()OVER(ORDER BY (SELECT NULL))),' ','0') n FROM(
select TOP(#NoOfRows) 1 x from sys.objects A,sys.objects B,sys.objects C,sys.objects D,sys.objects E,sys.objects F,sys.objects G
)X
)N
Query 1:
SELECT * FROM dbo.users
Results:
| ID | USER_ID | FIRST_NAME | LAST_NAME |
-----------------------------------------------------------
| 1 | User_0000000001 | John_0000000001 | Doe_0000000001 |
| 2 | User_0000000002 | John_0000000002 | Doe_0000000002 |
| 3 | User_0000000003 | John_0000000003 | Doe_0000000003 |
| 4 | User_0000000004 | John_0000000004 | Doe_0000000004 |
| 5 | User_0000000005 | John_0000000005 | Doe_0000000005 |
| 6 | User_0000000006 | John_0000000006 | Doe_0000000006 |
| 7 | User_0000000007 | John_0000000007 | Doe_0000000007 |
Just change the #NoOfRows to 300000 to get the number of rows you are looking for.
I've adapted a script found in this article:
DECLARE #RowCount INT
DECLARE #RowString VARCHAR(14)
DECLARE #First VARCHAR(14)
DECLARE #LAST VARCHAR(14)
DECLARE #ID VARCHAR(14)
SET #ID = 'UserID'
SET #First = 'John'
SET #Last = 'Doe'
SET #RowCount = 1
WHILE #RowCount < 300001
BEGIN
SET #RowString = CAST(#RowCount AS VARCHAR(10))
SET #RowString = REPLICATE('0', 6 - DATALENGTH(#RowString)) + #RowString
INSERT INTO TestTableSize (
UserID
,FirstName
,LastName
)
VALUES
(#ID + #RowString
, #First + #RowString
, #Last + #RowString)
SET #RowCount = #RowCount + 1
END

Resources