How to implement referential integrity here? - sql-server

I got the following structure - which I admit is not ideal, but so much is built on that, tat I want to minimize changes.
I am not sure about how to properly implement referential integrity between Documents and Delivery Adresses. Can it be done here without using triggers ? The problem is that the addressNum can sometimes be Null in the Documents.
CREATE TABLE [dbo].[Clients](
[IdClient] [varchar](10) NOT NULL,
[Nom] [varchar](40) NULL
CONSTRAINT PK_Clients PRIMARY KEY (IdClient))
GO
CREATE TABLE [dbo].[ClientsDelivAdr](
[IdClient] [varchar](10) NOT NULL,
[AdrNum] [tinyint] NOT NULL,
[Adresse] [varchar](200) NULL
CONSTRAINT [PK_ClientsAdrLivr] PRIMARY KEY (IdClient, AdrNum))
CREATE TABLE [dbo].[Documents](
[DocID] [int] IDENTITY(1,1) NOT NULL,
[NoDoc] [char](9) NULL,
[IdClient] [varchar](10) NULL,
[AdrNum] [tinyint] NULL,
[DateDoc] [smalldatetime] NOT NULL,
CONSTRAINT [PK_DocID] PRIMARY KEY (DocId))
Some Clients have several Delivery Adresses, some have none.
So data looks like this:
Clients
Id Name Address
--- ---- -------
AA ClientA addressA
BB ClientB qddressB
CC ClientC addressC
DeliveryAdresses
Client Adr Address
------ --- -------
AA 1 shop1
AA 2 shop2
CC 1 shopx
Documents
DocId Client Addr OrderDate
------- ------ ---- --------
1001 CC 1 5/5/2013
1002 AA 1 5/5/2013
1003 BB (Null) 5/5/2013

I think you can just use foreign keys as you would expect:
CREATE TABLE [dbo].[Documents](
[DocID] [int] IDENTITY(1,1) NOT NULL,
[NoDoc] [char](9) NULL,
[IdClient] [varchar](10) NULL,
[AdrNum] [tinyint] NULL,
[DateDoc] [smalldatetime] NOT NULL,
CONSTRAINT [PK_DocID] PRIMARY KEY (DocId),
CONSTRAINT FK_DOC_Clients FOREIGN KEY (IdClient)
references Clients (IdClient),
CONSTRAINT FK_Doc_Addresses FOREIGN KEY (IdClient,AdrNum)
references DeliveryAddresses (IdClient,AdrNum) )
If one or more column values in the referencing side of a foreign key is NULL, then the foreign key constraint is not checked. Conversely, there's no way to have NULL be a foreign key reference.

Related

JSON Many to Many RelationShip Group By

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

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)

How could I make a series of joins work with max value when aggregates do not work in them?

I'm looking only to get classification ids which are between the valid year range in classification. I'm using left joins because NULLs should be permitted.
I have tables:
CREATE TABLE classifications (
[id] [bigint] IDENTITY(1,1) NOT NULL,
[classification_code] [varchar](20) NOT NULL,
[description] [varchar](255) NULL,
[valid_from] [int] NULL,
[valid_to] [int] NULL
--Rest of constraints...
)
insert into classifications (classification_code, description, valid_from, valid_to)
values ('05012','Classification Number 1',2007,2012),
('05012','Classification Number 1',2013,2016),
('05012','Classification Number 1',2017,2020).
('12043','Classification Number 2',2007,2010),
('12043','Classification Number 2',2011,2020),
('12345','Classification Number 3',2013,2015),
('12345','Classification Number 3',2016,2020),
('54321','Classification Number 4',2007,2009),
('54321','Classification Number 4',2010,2013),
('54321','Classification Number 4',2014,2020)
CREATE TABLE comm_info_a (
[id] [bigint] IDENTITY(1,1) NOT NULL,
[comm_code] [nchar](10) NOT NULL, /*should be unique*/
[classification_code] [nchar](6) NULL,
[thing] [nchar](6) NULL
--Rest of constraints...
)
insert into comm_info_a (comm_code, classification_code)
values ('0100100000','54321'),
('8090010000','05012'),
('5002310010','12043'),
('0987654321','54321')
CREATE TABLE comm_info_b (
[id] [bigint] IDENTITY(1,1) NOT NULL,
[comm_code] [nchar](10) NOT NULL, /*should be unique*/
[classification_code] [nchar](6) NULL
--Rest of constraints...
)
insert into comm_info_b (comm_code, classification_code)
values ('0100100000','12043'),
('8090010000','00000'),
('5002310010','05012'),
('1234567890','12345')
CREATE TABLE transactions (
[comm_code] [varchar](50) NULL,
[year] [varchar](255) NULL
--Rest of constraints...
)
insert into transactions (comm_code, year) values
('0100100000', 2013),
('0100100000', 2015),
('0100100000', 2017),
('8090010000', 2009),
('8090010000', 2010),
('8090010000', 2011),
('8090010000', 2015),
('8090010000', 2017),
('8090010000', 2018),
('5002310010', 2008),
('5002310010', 2014),
And finally:
CREATE TABLE comm (
[id] [bigint] IDENTITY(1,1) NOT NULL,
[comm_code] [varchar](20) NULL, /*should be unique*/
[fk_classification_id_a] [bigint] NULL,
[fk_classification_id_b] [bigint] NULL
--Rest of constraints...
)
I am working on a query to insert comms from transactions, and comms should have unique comm_code
The query is as follows:
INSERT INTO comm
(comm_code,
fk_classification_id_a,
fk_classification_id_b)
SELECT comm_code,
ca.id,
cb.id,
MAX(year)
FROM transactions t
LEFT JOIN comm_info_a mia ON mia.comm_code=t.comm_code
LEFT JOIN comm_info_b mib ON mib.comm_code=t.comm_code
--these next two joins obviously do not work so I'm looking for something like it. Treat them as 'pseudo-code'
LEFT JOIN classifications ca ON ca.classification_code=mia.classification_code AND
MAX(t.year) BETWEEN ca.valid_from AND ca.valid_to
LEFT JOIN classifications cb ON cb.classification_code=mib.classification_code AND
MAX(t.year) BETWEEN cb.valid_from AND cb.valid_to
-- end of the two joins
WHERE NOT EXISTS
(SELECT DISTINCT comm_code FROM comm)
GROUP BY
t.comm_code
t.classification_code
So in the end I'm looking to get something like this as a result:
comm_code | fk_classification_id_a | fk_classification_id_b
-----------|------------------------|-----------------------
0100100000 | 5 | 10
8090010000 | 3 | NULL
5002310010 | 5 | 2
Please note that the comm_code is unique in this table!! Therefore: i want the comms on the newest transactions (thus the aggegate max year), and they should have the ids of the classification that the transaction year is in.
The real query is much more complex and longer but this pretty much covers all bases. Take a look into what is commented. I understand that it should be doable with a sub query of some sort, and I've tried, but so far I haven't found a way to pass aggregates to subqueries.
How could I tackle this problem?
Revised answer uses a common table expression to calculate the maximum year per comm_code and to exclude the comm_codes not wanted in the final result. After that the joins to the classification tables are straight forward as we have the comm_max_year value on each row to use in the joins.
with transCTE as (
select
t.*
, max(t.year) over(partition by comm_code) comm_max_year
from transactions t
left join comm on t.comm_code = comm.comm_code -- this table not in sample given
where comm.comm_code IS NULL -- use instead of NOT EXISTS
)
SELECT DISTINCT
t.comm_code
, ca.id as fk_classification_id_a
, cb.id as fk_classification_id_b
, t.comm_max_year
FROM transCTE t
LEFT JOIN comm_info_a mia ON mia.comm_code = t.comm_code
LEFT JOIN classifications ca ON mia.classification_code = ca.classification_code
AND t.comm_max_year BETWEEN ca.valid_from AND ca.valid_to
LEFT JOIN comm_info_b mib ON mib.comm_code = t.comm_code
LEFT JOIN classifications cb ON mib.classification_code = cb.classification_code
AND t.comm_max_year BETWEEN cb.valid_from AND cb.valid_to
ORDER BY
t.comm_code
;
GO
comm_code | fk_classification_id_a | fk_classification_id_b | comm_max_year
:--------- | :--------------------- | :--------------------- | :------------
0100100000 | 10 | 5 | 2017
5002310010 | 5 | 2 | 2014
8090010000 | 3 | null | 2018
Demo at dbfiddle here
CREATE TABLE transactions (
[comm_code] [varchar](50) NULL,
[year] [varchar](255) NULL
--Rest of constraints...
)
insert into transactions (comm_code, year) values
('0100100000', 2013),
('0100100000', 2015),
('0100100000', 2017),
('8090010000', 2009),
('8090010000', 2010),
('8090010000', 2011),
('8090010000', 2015),
('8090010000', 2017),
('8090010000', 2018),
('5002310010', 2008),
('5002310010', 2014)
;
GO
11 rows affected
CREATE TABLE classifications (
[id] [bigint] IDENTITY(1,1) NOT NULL,
[classification_code] [varchar](20) NOT NULL,
[description] [varchar](255) NULL,
[valid_from] [int] NULL,
[valid_to] [int] NULL
--Rest of constraints...
)
insert into classifications (classification_code, description, valid_from, valid_to)
values ('05012','Classification Number 1',2007,2012),
('05012','Classification Number 1',2013,2016),
('05012','Classification Number 1',2017,2020),
('12043','Classification Number 2',2007,2010),
('12043','Classification Number 2',2011,2020),
('12345','Classification Number 3',2013,2015),
('12345','Classification Number 3',2016,2020),
('54321','Classification Number 4',2007,2009),
('54321','Classification Number 4',2010,2013),
('54321','Classification Number 4',2014,2020)
;
GO
10 rows affected
CREATE TABLE comm_info_a (
[id] [bigint] IDENTITY(1,1) NOT NULL,
[comm_code] [nchar](10) NOT NULL, /*should be unique*/
[classification_code] [nchar](6) NULL,
[thing] [nchar](6) NULL
--Rest of constraints...
);
GO
✓
insert into comm_info_a (comm_code, classification_code)
values ('0100100000','54321'),
('8090010000','05012'),
('5002310010','12043'),
('0987654321','54321')
;
GO
4 rows affected
CREATE TABLE comm_info_b (
[id] [bigint] IDENTITY(1,1) NOT NULL,
[comm_code] [nchar](10) NOT NULL, /*should be unique*/
[classification_code] [nchar](6) NULL
--Rest of constraints...
);
GO
✓
insert into comm_info_b (comm_code, classification_code)
values ('0100100000','12043'),
('8090010000','00000'),
('5002310010','05012'),
('1234567890','12345');
GO
4 rows affected

Find out who didn't pay in a monthly billing system

I'm creating system for manage shops rent. I have 4 tables (customers, shops, contracts, payments).
And this is the 4 tables:
customers :
CREATE TABLE [dbo].[customers](
[cust_id] [int] NOT NULL,
[name] [nvarchar](50) NULL,
[nickname] [nvarchar](50) NULL,
[city] [nvarchar](50) NULL,
[phone1] [nvarchar](15) NULL,
[phone2] [nvarchar](15) NULL,
[phone3] [nvarchar](15) NULL,
[email] [nvarchar](50) NULL,
[image] [varbinary](max) NULL,
[date] [date] NULL,
[image_exist] [nvarchar](5) NULL,
shops :
CREATE TABLE [dbo].[shops](
[shop_id] [int] NOT NULL,
[size] [nvarchar](5) NULL,
[floor] [nvarchar](10) NULL,
[location] [nvarchar](50) NULL,
[status] [nvarchar](10) NULL,
[date] [date] NULL,
Contracts:
CREATE TABLE [dbo].[contracts](
[con_id] [int] NOT NULL,
[cust_id] [int] NULL,
[shop_id] [int] NULL,
[con_duration] [nvarchar](5) NULL,
[price] [nvarchar](50) NULL,
[con_use] [nvarchar](20) NULL,
[rent_type] [nvarchar](10) NULL,
[price2] [nvarchar](50) NULL,
[note2] [nvarchar](max) NULL,
[image] [image] NULL,
[date_start] [date] NULL,
[date_end] [date] NULL,
[note] [nvarchar](max) NULL,
[image_exist] [nvarchar](5) NULL,
Payments:
CREATE TABLE [dbo].[payments_monthly](
[id] [int] IDENTITY(1,1) NOT NULL,
[con_id] [int] NULL,
[pay_number] [nvarchar](50) NULL, \\ i get the duration from contact and make numbers of payments, if the duration was 12 then the pay_number will be 1,2,3,.....,12- so the customers need to pay 12 time , one time per month
[pay_value] [decimal](18, 0) NULL, \\ amount of payment
[pay_type] [nvarchar](50) NULL,
[ch_number] [int] NULL,
[note] [nchar](10) NULL,
[date] [date] NULL,
So what I need is get all customers that didn't pay for this month or specific month the user chose (note : not all customers must pay in the same date, maybe some need to pay on 10-9-2017 or some 20-9-2017) so I need all customers who must pay in the September (1-9-2017 to 30-92017).
So what I need to change in my tables to achieve my goals, or query.
I am working on a program to manage a group of shops and these shops rented for a certain period, so the contract is 6 months, 12 months, 18 or 24 ... etc.
there is 2 type of contracts:
1-(The contract is a certain period and the payment is one time)
2-(the contract is a certain period and the payment per month in a certain amount per month)
For example:
1: 12000 $ for 12 months - paid once (stored in payment_yearly table)
Contract 2: 12000 $ for 12 months - paid more than once - the payment will be as follows (12000/12 = 1000 $per month) (stored in the payment_monthly table)
The program worked as follows:
Table of tenants
And a table for rented shops
A table of contracts (in which the customers and the shops premises are connected)
After that, 2 tables for monthly payments and yearly payments. Payments are recorded for each contract (linked to the customer and the shop)
What if they're late and pay two months at the same time, so on Sept 1 you get payments for August and September? A traditional accounting system would also have an Invoices table where each month a new invoice is created, and when a payment is made it's applied to the appropriate invoice. Then it's a simple matter of looking for open invoices.
Here's how I would create a simple invoice table:
CREATE TABLE invoices (
ID INT IDENTITY(1,1) PRIMARY KEY
, con_id INT FOREIGN KEY REFERENCES dbo.Invoices(con_id)
, invoice_date DATETIME NOT NULL DEFAULT GETDATE()
, payment_id INT FOREIGN KEY REFERENCES dbo.payments(id)
)
It references both the contract and the payment record. On the first of each month, run a process that automatically generates a new invoice for each active contract. Then, when a payment comes in, add the payment ID to the invoice so you know it's paid.
This is a fairly simple system, though, because it assumes only one payment can be applied to one invoice, which only makes sense if nobody ever makes a partial payment. A better option would be to create another table with a many-to-many relationship between payments and invoices:
CREATE TABLE invoice_payments (
ID INT IDENTITY(1,1) PRIMARY KEY
, invoice_id INT FOREIGN KEY REFERENCES dbo.invoices(ID)
, payment_id INT REFERENCES dbo.payments(id)
, DateCreated DATETIME NOT NULL DEFAULT GETDATE()
)
Now you can apply any number of payments to any number of invoices, and you can create a query that adds up all invoices and all payments to ensure that the client has paid all of their obligations!
I think the following example might give you some pointers
DECLARE #PaymentMonth INT
-- For Semptember 2017 set the variable to
SET #PaymentMonth = 201709
-- For March 2016 set the variable to
-- SET #PaymentMonth = 201603
SELECT c.Name
FROM customers cust
INNER JOIN contracts cont ON cust.cust_id = cont.cust_id
LEFT JOIN payments_monthly p ON cont.con_id = p.con_id
WHERE DATEPART(year, p.date) * 100 + DATEPART(month, p.date) <> #PaymentMonth

is it possible to add duplicate values to usn column? this is the table i have created,how to add data something like i have shown in the example?

CREATE TABLE [dbo].[studentdb] (
[usn] VARCHAR (15) NOT NULL,
[name] VARCHAR (50) NOT NULL,
[collegename] VARCHAR (50) NOT NULL,
[eventid] VARCHAR (15) NOT NULL,
[passwd] VARCHAR (50) NULL,
[email] VARCHAR (75) NULL,
CONSTRAINT [PK_studentdb] PRIMARY KEY CLUSTERED ([usn] ASC, [eventid] ASC),
FOREIGN KEY ([eventid]) REFERENCES [dbo].[eventdb] ([eventid])
);
is it possible to add duplicate values to usn column?
this is the table i have created,how to add data something like i have shown in the example?
USN EVENTID
1 100
2 100
3 200
1 200
3 100
4 100
5 100
5 200
Your PRIMARY KEY is compound key. It is based on two columns ([usn] ASC, [eventid] ASC) so as long as pair is unique you can insert it.
In your example:
1 100
2 100
3 200
1 200
3 100
4 100
5 100
5 200
every pair is unique.
For inserting data use INSERT INTO syntax like:
INSERT INTO [dbo].[studentdb](
[usn],
[name],
[collegename],
[eventid],
[passwd],
[email])
VALUES (1, 100, ...), -- rest of values
(2, 100, ...),
...;

Resources