SQL Server how to make a good structured database with different tables - sql-server

I want to create an application that loads data from a database by selecting month and year: https://gyazo.com/8a08f56231900945d20dc3801993834a
The thing is that I don't know how the structure of the database.
For example: I need to know the salary of an employee of the year January 2010 and February 2015. Do I need to create tables tbl2010January and tbl2015February? That seems to be ultra noob to create 12 different tables for each year.
The problem that I also might have is how do I make a relation of an employee in every table?
For example: does an Employee1 need to have a relation with every table from tbl2010January till tbl2016December?
Can someone please help me out with this problem? I'm not so experienced with programming but am eager to learn!

All you would need is a singular salary table with either (Employee Id, Year, Month) OR (Employee Id, Date)
You'll need to create some indexes on Employee ID and the date or year/month.
This way you can create queries to either sum the entire year and get individual months without the need for separate tables.
CREATE TABLE dbo.Salary
(
fkEmployeeId int,
Salary decimal,
SalaryYear int,
SalaryMonth int
);
CREATE INDEX IX_Salary_Employee_Date ON Salary(fkEmployeeId, SalaryYear, SalaryMonth)
DECLARE #Year int = 2016
DECLARE #Month int = 1
SELECT *
FROM Salary s
INNER JOIN MyEmployeeTable e on e.pkEmployeeId = s.fkEmployeeId
WHERE SalaryYear = #Year and SalaryMonth = #Month

Related

SQL : Set operation to perform logic for each record

Table 1 - Escalation, Table2 - Data
The requirement is : for all record in data table, we need to perform escalation.
Example 1 - record 1 in data table, year is 2014 and economic year is 2018. So, we need to escalate the value(600) from 2014 to 2015, then to 2016, then to 2017.
So, final value will be 600*5*6*7
Example 2 - record 3 in data table will be escalated twice - from 2015 to 2016 and then to 2017. So, final value will be 1000*6*7
This has to be performed for all records in data table.
I don't want to use cursor as i have 3 million records to do this.
Please suggest some idea to perform using Set Operation
create table data
([year] int,
value int,
economic_year int,
modelid int,
shopid int)
create table escalation
([year] int,
shopid int,
value_es int
)
insert into data
values(
2014,600,2018,5,1),
(2014,600,2018,5,1),
(2015,1000,2018,5,1),
(2016,2000,2018,5,1),
(2017,3000,2018,5,1)
insert into escalation
values
(2014,1,4),
(2015,1,5),
(2016,1,6),
(2017,1,7)
select * from escalation
select * from data
One way you could do this is have a third table that you prepopulate with all the possible "update paths" and the respective values. For example, if your year is 2014 and economic year is 2018, then your multiplier (value_es) is 120 (4*5*6). Something like this:
You would join the data table to this new table on two columns data.year = start_year and data.economic_year = econ_year. Then, your new value would be data.value * val.
You could create a simple ETL process that initially populates this new table and updates it when the calendar rolls over to a new year. Let me know what you think or if you have any questions.

T-SQL create label “week #1: 1/1/18 - 1/7/18”

Is there a way to create a table with the following?
Label
“week #1: 1/1/18 - 1/7/18”
“week #2: 1/8/18 - 1/15/18”
And so forth?
Basically, I’m looking for the week number and the date range that week includes.
I think what you want as a starting point, is a "date dimension" or "calendar table". Here's one of many examples for creating them (creating them is not really the issue though, it's how you use them that's more important).
In your example, it looks like you want to pivot the data (create a crosstab). As a rule of thumb, you're generally better off pivoting on the client application, than you are persisting that denormalised anti-pattern in a relational database.
Here's a fictitious example:
DECLARE #start_date as datetime = '20180301';
DECLARE #end_date as datetime = dateadd(dd,datediff(dd,0,GETDATE()),0);--midnight last night
SELECT cal.week_starting --The date of the start of the week eg 15 April 2018.
,dateadd(d,6,cal.week_starting) as week_ending -- The date of the last day of the week eg 21 April 2018. You can cast as varchar, format and concatenate to the previous field to suit yourself.
,my_events.my_category
,count(*) as recs
FROM my.CALENDAR cal
JOIN dbo.big_list_of_events my_events ON cal.census_dttm = my_events.event_date
WHERE my_events.event_date >= #start_date
and my_events.event_date < #end_date
GROUP BY cal.week_starting
,my_events.my_category
ORDER BY cal.week_starting
,my_events.my_category
;
Once you get to this point you're ready to query it with your client application (eg Pivot Tables in Excel) and slice and dice to your heart's content. Again, you probably don't want data stored in your db as a crosstab.

Date range based on Column Date

I am using the latest SQL Server. I have a table with a CreatedDate column. I need to write a Query that uses dates that are plus or minus 7 from the Date in CreatedDate. I have no clue how to go about this. My thought was this:
DECLARE #Date datetime
DECLARE #SevenBefore datetime
DECLARE #SevenAfter datetime
SET #Date = CreatedDate
SET #SevenBefore = DATEADD(day,-7,#Date)
SET #SevenAfter = DATEADD(day,7,#Date)
SELECT *
FROM <table>
WHERE <table> BETWEEN #SevenBefore AND #SevenAfter
The issue with this is that I cannot use "CreatedDate" as a SET #DATE because SQL gives an error "Invalid column name 'CreatedDate'"
Any help would be appreciated. I cannot list a date because every date in that column could be different.
Thanks
In this case, you need to stop thinking as a programmer would, and start thinking as a Database programmer would.
Lets work only with this central part of your query:
SELECT *
FROM <table>
WHERE <table> BETWEEN #SevenBefore AND #SevenAfter
Now, you say that the CreatedDate is a column in a table. For this example, I will assume that the CreatedDate is in a table other than the one in your example above. For this purpose, I will give two fake names to the tables. The table with the CreatedDate, I will call tblCreated, and the one from the query above I will call tblData.
Looking above, it's pretty obvious that you can't compare an entire table row to a date. There must be a field in that table that contains a date/time value. I will call this column TargetDate.
Given these assumptions, your query would look something like:
SELECT *
FROM tblCreated tc
INNER JOIN tblData td
ON td.TargetDate BETWEEN DATEADD(day, -7, tc.CreatedDate) and DATEADD(day, 7, tc.CreatedDate)
Looking at this, it is clear that you still need some other associations between the tables. Do you only want all data rows per customer based on the Created date, or perhaps only want Creations where some work was done on them as shown in the Data records, or ??. Without a fuller specification, we can't help with that, though.

Problèm with SQL select grouping

I have a small problem with a SQL Server query.
I have an issue with my view of several base tables with duplicate values, so far no problem, these duplicates are logical. By unfortunately I do not get the desired end result, I could do it by programming the front end of my application but I would prefer to do the work on the server.
I will explain the principle:
I have 30 companies which each have an employee table.
My view is a union of the 30 employee tables.
Each employee has a unique serial number, the number is the same across tables, so an employee named "John Doe" with an ID number 'S0000021' can be hired in Company A then transferred to company Q without any problems, it will retain the serial number 'S0000021'.
The difference between the data from the Employee tables A and Q will be in this example the start (hire) and release (transfer) dates entered for Company A and just the start date for company Q so the view will have 2 lines for "John Doe".
12 common fields are the following:
Serial Number (Identical in every employee table)
Social Security Number (Same in every employee table)
Start/Hire Date
Release/Transfer date (empty/null if the employee is current)
Name (Can change across companies if the person divorces)
First name
Maiden name
Last Name
Gender
Final Released
Company Code
The problem seems simple that I would not appear that the latest information of the employee, except with a group by, if it has changed name or release date, it will be displayed twice.
I tried the following different ways but they don't return what I want
I returned results both ways but I always see duplicates because my dates within companies are never identical, and their name may change.
Sorry for this Google translation.
1 --
select
vue.matricule,
vue.numsecu,
vue.name,
vue.lastname,
vue.maidenname,
vue.secondname,
vue.genre,
vue.released,
vue.companycode
from
vue
group by
vue.matricule,
vue.numsecu,
vue.name,
vue.lastname,
vue.maidenname,
vue.secondname,
vue.genre,
vue.released,
vue.companycode
2---
select
distinct(vue.matricule),
vue.numsecu,
vue.name,
vue.lastname,
vue.maidenname,
vue.secondname,
vue.genre,
vue.released,
vue.companycode
from
vue
I assumed the following:
there is a view (vue) that already gathers all data from each of the 30 companies
you are just looking for the latest record for each employee
If you need to also see a record for each name change we can change this.
--set up test data
declare #vue table (
matricule varchar(20),
numsecu varchar(20),
name varchar(20),
lastname varchar(20),
maidenname varchar(20),
secondname varchar(20),
genre varchar(20),
start datetime,
released datetime,
companycode varchar(20));
insert #vue values
('S0000021','123456789','John', 'Doe',null,null,'M','2015-01-01','2015-12-31','A'),
('S0000021','123456789','Johnny', 'Doe',null,null,'M','2016-01-01',null,'Q'), --new company, name change, currently employed
('S0000022','123456780','Jane', 'Doe',null,null,'M','2015-01-01','2015-12-31','A'),
('S0000022','123456780','Jane', 'Doe',null,null,'M','2016-01-01','2016-02-01','Q'); --new company, name change, terminated
select * from #vue order by matricule, start;
--get latest record for each employee
select *
from (--add row numbering
select *, row_number() over (partition by matricule order by start desc) row_num
from #vue
) vue2
where vue2.row_num = 1;

Index on Date field in Calendar table

I have a calendar table that has a list of all days frtom 01-JAN-1990 to 31-DEC-2050
That results in 22279 rows in my table.
A lot of queries we do, I join to the calendar as I need a list of dates based on certain data. For example:
SELECT ...
FROM Person A
INNER JOIN Calendar C
ON C.DateValue BETWEEN A.StartDate and A.EndDate
This is an example... but I'm looking for a list of the dates for the person, and a date column to come back.
What I'd like to know, is: Is the DateValue column a good candicate for an Index? And would there be ebefit of it being Clustered?
(SQL Server 2008 R2)
No, the Date type columns is not good candidate. Columns which you want to choose should be more simple. like int or BigInt types.

Resources