JSON Many to Many RelationShip Group By - sql-server

I'm trying to create an SQL query allowing me to do this:
I have 3 tables in SQL Server 2017:
CREATE TABLE [dbo].[PRODUCTCATEGORY]
(
[PROD_ID] [int] NOT NULL,
[CAT_ID] [int] NOT NULL
CONSTRAINT [PK_PRODUCTCATEGORY]
PRIMARY KEY CLUSTERED ([PROD_ID] ASC, [CAT_ID] ASC)
)
CREATE TABLE [dbo].[CATEGORY]
(
[CAT_ID] [int] IDENTITY(1,1) NOT NULL,
[CAT_TITLE] [varchar](50) NOT NULL
CONSTRAINT [PK_CATEGORY]
PRIMARY KEY CLUSTERED ([CAT_ID] ASC)
)
CREATE TABLE [dbo].[PRODUCT]
(
[PROD_ID] [int] IDENTITY(1,1) NOT NULL,
[PROD_TITLE] [varchar](50) NOT NULL
CONSTRAINT [PK_PRODUCT]
PRIMARY KEY CLUSTERED ([PROD_ID] ASC)
)
A product can have 1 to many categories
A category can have 1 to many products
PROD_ID
PROD_TITLE
1
Book 1
2
Book 2
CAT_ID
CAT_TITLE
1
Cat 1
2
Cat 2
3
Cat 3
PROD_ID
CAT_ID
1
1
1
2
2
1
2
3
I would like to retrieve this:
| CAT_ID |CAT_TITLE | PRODUCTS |
|:------- |:--------:|:------------------------------------------------------------------------|
| 1 | Cat 1 |[{"PROD_ID":1,"PROD_TITLE":"Book 1"},{"PROD_ID":2,"PROD_TITLE":"Book 2"}]|
| 2 | Cat 2 |[{"PROD_ID":1,"PROD_TITLE":"Book 1"}] |
| 3 | Cat 3 |[{"PROD_ID":2,"PROD_TITLE":"Book 2"}] |
Thanks for your help

I just found this, using FOR JSON:
https://learn.microsoft.com/en-us/sql/relational-databases/json/format-query-results-as-json-with-for-json-sql-server?view=sql-server-ver15
I think something like this might work:
SELECT c.CAT_ID, c.CAT_TITLE,
(
SELECT p.PROD_ID, p.PROD_TITLE
FROM PRODUCT p
JOIN PRODUCTCATEGORY pc ON pc.PROD_ID = p.PROD_ID
WHERE pc.CAT_ID = c.CAT_ID
FOR JSON PATH
) AS ProductsAsJson
FROM CATEGORY c

Related

Check in and check out time in SQL

simple data of table
My table is:
SELECT TOP (1000)
[ID]
,[UserName]
,[CheckTime]
,[Checktype]
,[CheckinLocation]
,[lat]
,[lng]
FROM
[dbo].[CheckTime]
INSERT INTO [dbo].[CheckTime] ([UserName], [CheckTime], [Checktype],[CheckinLocation], [lat], [lng])
VALUES (<UserName, nchar(10),>
,<CheckTime, datetime,>
,<Checktype, nvarchar(50),>
,<CheckinLocation, nvarchar(50),>
,<lat, float,>
,<lng, float,>)
GO
Create table script:
CREATE TABLE [dbo].[CheckTime]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[UserName] [nchar](10) NULL,
[CheckTime] [datetime] NULL,
[Checktype] [nvarchar](50) NULL,
[CheckinLocation] [nvarchar](50) NULL,
[lat] [float] NULL,
[lng] [float] NULL,
CONSTRAINT [PK_CheckTime]
PRIMARY KEY CLUSTERED ([ID] ASC)
WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
I need to select each distinct home holding the maximum value of datetime.
max CheckTime as check out
min CheckTime as check in
I need a result like this:
id | Username | check in | check out
---+----------+-------------------+-------------------
1 | 10 | 2017-1-2 08:02:05 | 2017-1-2 10:02:05
1 | 12 | 2017-1-2 08:02:05 | 2017-1-2 10:02:05
1 | 12 | 2017-1-3 08:02:05 | 2017-1-3 10:02:05
1 | 10 | 2017-1-3 08:02:05 | 2017-1-3 10:02:05
I have tried:
You can try the following query.
Select Username
, Cast(CheckTime as Date) as CheckDate
, min(CheckTime) as [check in]
, max(CheckTime) as check out
From CheckInTable
Group by id, Username, Cast(CheckTime as Date)

Displaying SQL SELECT rows in a table display

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
}
]
}
]

Possible duplicates to find in SQL

I've been trying the following suggestions from this post, but this does not apply for my case or at least I am not capable of adapting the query to my needs.
I have three tables: one stands for documents header, one stands for documents lines and one stands for item's information (Code, Description etc).
I would like to extract all the documents number that have the same value (this is the information from the documents header table), the same items (the code of the item) and the same quantity (from the lines of the documents tables). How can I extract this information? Thanks
The tables are -
DocHeader DocLines Items
ID fDocID ID
Code fItemID Code
Date Quantity Description
---- -------- -----------
TotalValue etc etc
Later edit
Output should like something like:
DocCode ItemCode Quantity TotalValue
01 001 5 1000
01 002 5 1000
01 003 4 1000
02 001 5 1000
02 002 5 1000
02 003 4 1000
DDL
create table DocHeader
(
Id bigint not null identity(1,1) primary key clustered
, Code nvarchar(32) not null
, [Date] datetime not null
)
go
create table Items
(
Id bigint not null identity(1,1) primary key clustered
, Code nvarchar(32) not null
, [Description] nvarchar(256)
, UnitPrice money not null
)
go
create table DocLines
(
Id bigint not null identity(1,1) primary key clustered
,fDocId bigint not null constraint fk_DocLines_fDocId foreign key references DocHeader(Id)
,fItemId bigint not null constraint fk_DocLines_fDocId foreign key references Items(Id)
,Quantity int not null
)
go
create view vDocHeader as
select dh.*
, x.TotalValue
from DocHeader
left outer join
(
select dl.fDocId
, sum(dl.Quantity * i.UnitPrice) TotalValue
from DocLines dl
inner join Items i
on i.Id = dl.fItemId
group by dl.fDocId
) x
on x.fDocId = dh.Id
Why not simple grouping?
SELECT dh.Code AS DocCode, i.Code AS ItemCode, Quantity, SUM(dl.Quantity * i.UnitPrice) AS TotalValue
FROM DocHeader dh
LEFT JOIN DocLines dl ON dl.fDocId=dh.id
JOIN Items i ON i.Id = dl.fItemId
GROUP BY dh.Code,i.Code,Quantity

SQL where clause for self-referencing table

I have something like the following table:
CREATE TABLE [dbo].[Test]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](450) NULL,
[Description] [nvarchar](4000) NULL,
[Created] [datetime] NULL,
[OrgId] [int] NULL CONSTRAINT [FK_Test_OrgId] FOREIGN KEY REFERENCES Test(Id),
CONSTRAINT [PK_Test]
PRIMARY KEY CLUSTERED ([Id] ASC)
)
A new entry has OrgId = null. If an entry has been edited, a new row is created with OrgId set to its original parent. If an entry is edited multiple times, all children will have OrgId set to the Id of the original row. The created datetime provides the "order".
What I need to do is select only the newest versions.
Given the table below, I am looking to select only Id 3, 5 & 6
Id Title Description Created PreId
-----------------------------------------------------
1 Car Orginal car 2014-01-01 NULL
2 House Original house 2014-01-01 NULL
3 Bike Original bike 2014-01-01 NULL
4 Car Car updated 2014-06-01 1
5 Car Car updated again 2014-08-01 1
6 House house updated 2014-09-01 2
Any input appreciated.
Thanks.
Since all records are pointing at the original row (and not the previous one) :
SELECT ID, Title, DEscription, CREATED, PreID
FROM
(SELECT ID, Title, DEscription, CREATED, PreID,
ROW_NUMBER() over(partition by ISNULL(OrgID,id) order by id desc) rn
FROM test) A
where RN = 1
WITH Titles AS
(
SELECT DISTINCT Title
FROM dbo.Test
)
SELECT A.ID, Ti.Title, A.[Description], A.Created, A.OrgId
FROM Titles AS Ti
OUTER APPLY (SELECT TOP (1) ID, [Description], [Created], [OrgId]
FROM dbo.Test AS Te
WHERE Te.Title = Ti.Title
ORDER BY Created DESC
) AS A;

SQL Server join query not retrieving proper result

Select the list of all dealers whose vehicles are never assigned to any customer i.e that vehicle is not present in PS3_VehicleBooking, and committed date is greater than today's date.
3 tables to be used are-
CREATE TABLE PS3_Dealer(
ID int IDENTITY(1,1) NOT NULL Primary KEY,
DealerID varchar(255) NULL,
DealerName varchar(255) NOT NULL,
ContactNo varchar(255) NOT NULL,
NoOfVehicles bigint NOT NULL,
CommittedDate date NOT NULL
);
create table PS3_Vehicle
(
ID int IDENTITY(1,1) NOT NULL Primary Key,
DealerID varchar(255) NOT NULL,
VehicleID varchar(255) UNIQUE NOT NULL,
VehicleName varchar(255) NOT NULL,
Capacity BIGINT NOT NULL
);
create table PS3_VehicleBooking
(
ID int IDENTITY(1,1) NOT NULL Primary Key,
BookingID varchar(255) NOT NULL UNIQUE,
VehicleID varchar(255) NOT NULL,
StartDate date NOT NULL,
EndDate date NOT NULL
);
PS3_Dealer values-
DealerID DealerName ContactNo NoOfVehicles CommittedDate
1 ng 9 5 2013-12-12
2 nikki 5 7 2013-12-25
3 nik 4 6 2013-10-11
4 hj 2 2 2014-11-10
5 pk 67 8 2013-10-10
PS3_Vehicle
DealerID VehicleID VehicleName Capacity
1 V1 ind 4
2 V2 innova 5
3 V3 innova 6
1 V4 ula 8
4 V5 hkk 2
5 V6 ghi 9
2 V7 bjhgi 4
PS3_VehicleBooking-
BookingID VehicleID StartDate EndDate
1 V1 2013-12-13 2013-12-17
2 V2 2013-10-11 2013-10-13
3 V3 2014-12-10 2014-12-13
4 V4 2012-10-10 2012-10-13
5 V2 2013-12-14 2013-12-18
expected outcome-
DealerID DealerName ContactNo NoOfVehicles CommittedDate
4 hj 2 2 2014-11-10
but i am getting-
DealerID DealerName ContactNo NoOfVehicles CommittedDate
4 hj 2 2 2014-11-10
2 Nikki 5 7 2013-12-25
i dont want dealer id 2 in result because V2 provided by dealer id 2 is present in PS3_Booking.
My query is-
SELECT h.DealerID,
h.DealerName,
h.ContactNo,
h.NoOfVehicles,
h.CommittedDate
FROM PS3_Dealer h
INNER JOIN(SELECT DealerID,
PS3_VehicleBooking.VehicleID
FROM PS3_Vehicle
LEFT JOIN PS3_VehicleBooking
ON PS3_Vehicle.VehicleID = PS3_VehicleBooking.VehicleID) w
ON h.DealerID = w.DealerID
WHERE w.VehicleID IS NULL
AND h.CommittedDate > GETDATE()
please correct where i am wrong?
The problem is that DealerID 2 has 2 Vehicles. One that has booking, and the other does not. When you run
SELECT DealerID,
PS3_VehicleBooking.VehicleID
FROM PS3_Vehicle
LEFT JOIN PS3_VehicleBooking
ON PS3_Vehicle.VehicleID = PS3_VehicleBooking.VehicleID
You will get one record that has DealerID 2 with NULL VehicleID and the other none NULL Vehicle ID.
You can try to the run the statement below to get the desired results (may not be the most optimal, but it is a fix to your statement):
SELECT h.DealerID,
h.DealerName,
h.ContactNo,
h.NoOfVehicles,
h.CommittedDate
FROM PS3_Dealer h
INNER JOIN(SELECT DealerID,
MAX(PS3_VehicleBooking.VehicleID) as VehicleID
FROM PS3_Vehicle
LEFT JOIN PS3_VehicleBooking
ON PS3_Vehicle.VehicleID = PS3_VehicleBooking.VehicleID
GROUP BY DealerID
) w
ON h.DealerID = w.DealerID
WHERE w.VehicleID IS NULL
AND h.CommittedDate > GETDATE()
Edited to actually answer the question
You were getting Nikki because there does exist a car for her (V7) that is not in the booking table.
This should exclude Nikki.
SELECT h.DealerID,
h.DealerName,
h.ContactNo,
h.NoOfVehicles,
h.CommittedDate
FROM PS3_Dealer d
WHERE NOT EXISTS
(SELECT *
FROM PS3_VehicleBookingb
JOIN PS3_Vehiclev ON b.vehicleID = v.VehicleID
JOIN PS3_Dealerd2 ON d2.dealerID = v.dealerID
WHERE d2.dealerID = d.dealerID)
AND CommittedDate > Current_date()
http://sqlfiddle.com/#!2/fb365/19

Resources