How to make a summarized group by using SQL? - sql-server

I created a SQL stored procedure:
CREATE PROCEDURE [stored procedure name]
(#Location [varchar](255),
#start_date datetime,
#end_date datetime)
AS
BEGIN
SELECT id, location, name, begin_date, age, career, phone_number
FROM information
WHERE [begin_date] BETWEEN #start_date AND #end_date
AND [location] = #Location
END
And there are different groups in location: Shanghai, New York, Toronto, Sydney, Tokyo. But now I would like to have one more category: All. This category will display all locations. So I can use it in SSRS.
What I wrote:
select 1 as sort_order, 'All' as location
union
select distinct 2 as sort_order, location from information
order by sort_order, location
But this will only return a dropdown list of:
"All", "Shanghai", "New York", "Toronto", "Sydney", "Tokyo"
When I click on Shanghai, New York, Toronto, Sydney, Tokyo, it works fine and display corresponding data. But when I click on "All", there is no data showing up. How can I alter my code to achieve the goal?

I think you need to take All into account:
and ([location] = #Location or #location = 'All')
Often, a NULL value is used, to avoid conflict with valid values:
and ([location] = #Location or #location is null)

Related

SQL Server: table design for changing Identity records

I have a view which displays test data from multiple sources for a GPS spot.
The view displays the "GPS Point ID" and some geological test results associated with this GPS Point.
The GPS-POINT-ID is like this : XYZ-0XX-CCCCC
XYZ : Area
00XX : ID
CCCC: Coordinates
The GPS point name changes over time, the first portion of point name(XYZ-0XX) is same and doesn't change, but the Coordinate part (CCCC) changes according to new GPS point location.
I wanted to design a table that will have the previously mentioned view as a datasource. I need to decide about the following:
Primary key: if I used the full GPS-POINT-ID, I won't be able to keep track of the changes because it changes frequently over time. I can't keep track of the point. and I can't link it to it's historical records.
If I use the fixed part of GPS-Point-ID (XYZ-00XX) as a computed column, I can't use it as a primary key, because the same point has many historical records that have the same (XYZ-00XX) part, this will violate the primary key duplicate constraint.
If I create an identity column that will increase for each new record, how can I keep track of each point name change and get the latest test data as well as historical data for each point (XYZ-00XX).
Sample rows from the view are attached in a snapshot.
Thanks
I would recommend using identity for primary key with no business value. I would store the data in two columns one with the static data and another with the changing data. Then you can have a computed column that puts them together as one field if that is necessary. You can also add a date field so that you can follow the history. The static data column being the identifier that ties the records together.
I am assuming you do not want to use auditing to track historical records for some reason. That is the approach I would normally take.
http://weblogs.asp.net/jongalloway/adding-simple-trigger-based-auditing-to-your-sql-server-database
EDIT:
Sample query works if only one update can happen on a given date. If more than one update can occur then the row_number function can be used instead of group by.
Select *
From Table T1
Join (Select Max(MatchDate) MatchDate, GpsStaticData
From Table Group By GpsStaticData) T2
On T1.GpsStaticData = T2.GpsStaticData And T1.UpdateDate = T2.MatchDate
EDIT:
Using Row_Number()
With cteGetLatest As
(
Select UpdateDate MatchDate, GpsStaticData,
Row_Number() Over (Partition By GpsStaticData, Order By UpdateDate Desc) SortOrder
)
Select *
From Table T1
Join (Select MMatchDate, GpsStaticData
From cteGetLatest Where SortOrder = 1) T2
On T1.GpsStaticData = T2.GpsStaticData And T1.UpdateDate = T2.MatchDate
You can add more fields after Order By UpdateDate in the row_number function to determine which record is selected.
--To avoid artificial columns overhead costs a compound Primary Key can be used:
-- Simulate the Source View
CREATE TABLE ybSourceView (
[GPS-POINT-ID] VARCHAR(20),
[Status] NVARCHAR(MAX),
UpdateDate [datetime2],
Reason NVARCHAR(MAX),
OpId VARCHAR(15)
);
-- Source View sample data
INSERT INTO ybSourceView ([GPS-POINT-ID], [Status], UpdateDate, Reason, OpId)
VALUES ('RF-0014-9876', 'Reachable' , '2015-01-27 13:36', 'New Updated Coordinate' , 'AFERNANDO'),
('RF-0014-9876', 'Reachable' , '2014-02-27 09:37', 'New Updated Coordinate' , 'AFERNANDO'),
('RF-0014-3465', 'Reachable' , '2015-04-27 09:42', 'New Updated Coordinate' , 'HRONAULD' ),
('RF-0014-2432', 'Reachable' , '2013-06-27 12:00', 'New Updated Coordinate' , 'AFERNANDO'),
('RF-0015-9876', 'OUT_OF_Range', '2014-04-14 12:00', 'Point Abandoned, getting new coordinate', 'AFERNANDO');
-- Historic Data Table
CREATE TABLE ybGPSPointHistory (
Area VARCHAR(5) NOT NULL DEFAULT '',
ID VARCHAR(10) NOT NULL DEFAULT '',
Coordinates VARCHAR(20) NOT NULL DEFAULT '',
[GPS-POINT-ID] VARCHAR(20),
[Status] NVARCHAR(MAX),
UpdateDate [datetime2] NOT NULL DEFAULT SYSUTCDATETIME(),
Reason NVARCHAR(MAX),
OpId VARCHAR(15),
CONSTRAINT ybGPSPointHistoryPK PRIMARY KEY (Area, ID, UpdateDate) --< Compound Primary Key
);
GO
-- Update Historic Data Table from the Source View
INSERT INTO ybGPSPointHistory (Area, ID, Coordinates, [GPS-POINT-ID], [Status], UpdateDate, Reason, OpId)
SELECT LEFT(Src.[GPS-POINT-ID], LEN(Src.[GPS-POINT-ID]) - 10), RIGHT(LEFT(Src.[GPS-POINT-ID], LEN(Src.[GPS-POINT-ID]) - 5), 4), RIGHT(Src.[GPS-POINT-ID], 4), Src.[GPS-POINT-ID], Src.[Status], Src.UpdateDate, Src.Reason, Src.OpId
FROM ybSourceView Src
LEFT JOIN ybGPSPointHistory Tgt ON Tgt.[GPS-POINT-ID] = Src.[GPS-POINT-ID] AND Tgt.UpdateDate = Src.UpdateDate
WHERE Tgt.[GPS-POINT-ID] Is NULL;
--Tests (check Actual Execution Plan to see PK use):
-- Full history
SELECT * FROM ybGPSPointHistory;
-- Up-to-date only
SELECT *
FROM (
SELECT *, RANK () OVER (PARTITION BY Area, ID ORDER BY UpdateDate DESC) As HistoricOrder
FROM ybGPSPointHistory
) a
WHERE HistoricOrder = 1;
-- Latest record for a particular ID
SELECT TOP 1 *
FROM ybGPSPointHistory a
WHERE [GPS-POINT-ID] = 'RF-0014-9876'
ORDER BY UpdateDate DESC;
-- Latest record for a particular ID in details (more efficient)
SELECT TOP 1 *
FROM ybGPSPointHistory a
WHERE Area = 'RF' AND ID = '0014' AND Coordinates = '9876'
ORDER BY UpdateDate DESC;
-- Latest record for a particular point
SELECT TOP 1 *
FROM ybGPSPointHistory a
WHERE Area = 'RF' AND ID = '0014'
ORDER BY UpdateDate DESC;
--Clean-up:
DROP TABLE ybGPSPointHistory;
DROP TABLE ybSourceView;

SQL use a variable as TABLE NAME in a FROM

We install our database(s) to different customers and the name can change depending on the deployment.
What I need to know is if you can use a variable as a table name.
The database we are in is ****_x and we need to access ****_m.
This code is part of a function.
I need the #metadb variable to be the table name - Maybe using dynamic SQL with
sp_executesql. I am just learning so take it easy on me.
CREATE FUNCTION [dbo].[datAddSp] (
#cal NCHAR(30) -- calendar to use to non-working days
,#bDays INT -- number of business days to add or subtract
,#d DATETIME
)
RETURNS DATETIME
AS
BEGIN
DECLARE #nDate DATETIME -- the working date
,#addsub INT -- factor for adding or subtracting
,#metadb sysname
SET #metadb = db_name()
SET #metadb = REPLACE (#metadb,'_x','_m')
SET #metadb = CONCAT (#metadb,'.dbo.md_calendar_day')
SET #ndate = #d
IF #bdays > 0
SET #addsub = 1
ELSE
SET #addsub = -1
IF #cal = ' ' OR #cal IS NULL
SET #cal = 'CA_ON'
WHILE #bdays <> 0 -- Keep adding/subtracting a day until #bdays becomes 0
BEGIN
SELECT #ndate = dateadd(day, 1 * #addsub, #ndate) -- increase or decrease #ndate
SELECT #bdays = CASE
WHEN (##datefirst + datepart(weekday, #ndate)) % 7 IN (0, 1) -- ignore if it is Sat or Sunday
THEN #bdays
WHEN ( SELECT 1
FROM #metadb -- **THIS IS WHAT I NEED** (same for below) this table holds the holidays
WHERE mast_trunkibis_m.dbo.md_calendar_day.calendar_code = #cal AND mast_trunkibis_m.dbo.md_calendar_day.calendar_date = #nDate AND mast_trunkibis_m.dbo.md_calendar_day.is_work_day = 0
) IS NOT NULL -- ignore if it is in the holiday table
THEN #bdays
ELSE #bdays - 1 * #addsub -- incr or decr #ndate
END
END
RETURN #nDate
END
GO
The best way to do this, if you aren't stuck with existing structures is to keep all of the table structures and names the same, simply create a schema for each customer and build out the tables in the schema. For example, if you have the companies: Global Trucking and Super Store you would create a schema for each of those companies: GlobalTrucking and SuperStore are now your schemas.
Supposing you have products and payments tables for a quick example. You would create those tables in each schema so you end up with something that looks like this:
GlobalTrucking.products
GlobalTrucking.payments
and
SuperStore.products
SuperStore.payments
Then in the application layer, you specify the default schema name to use in the connection string for queries using that connection. The web site or application for Global Trucking has the schema set to GlobalTrucking and any query like: SELECT * FROM products; would actually automatically be SELECT * FROM GlobalTrucking.products; when executed using that connection.
This way you always know where to look in your tables, and each customer is in their own segregated space, with the proper user permissions they will never be able to accidentally access another customers data, and everything is just easier to navigate.
Here is a sample of what your schema/user/table creation script would look like (this may not be 100% correct, I just pecked this out for a quick example, and I should mention that this is the Oracle way, but SQL Server should be similar):
CREATE USER &SCHEMA_NAME IDENTIFIED BY temppasswd1;
CREATE SCHEMA AUTHORIZATION &SCHEMA_NAME
CREATE TABLE "&SCHEMA_NAME".products
(
ProductId NUMBER,
Description VARCHAR2(50),
Price NUMBER(10, 2),
NumberInStock NUMBER,
Enabled VARCHAR2(1)
)
CREATE TABLE "&SCHEMA_NAME".payments
(
PaymentId NUMBER,
Amount NUMBER(10, 2),
CardType VARCHAR2(2),
CardNumber VARCHAR2(15),
CardExpire DATE,
PaymentTimeStamp TIMESTAMP,
ApprovalCode VARCHAR2(25)
)
GRANT SELECT ON "&SCHEMA_NAME".products TO &SCHEMA_NAME
GRANT SELECT ON "&SCHEMA_NAME".payments TO &SCHEMA_NAME
;
However, with something like the above, you only have 1 script that you need to keep updated for automation of adding new customers. When you run this, the &SCHEMA_NAME variable will be populated with whatever you choose for the new customer's username/schemaname, and an identical table structure is created every time.

Search data from database according to given data using SQL Server

I want to retrieving data from database according to my given data in search field using SQL server.I am explaining the scenario below. Suppose I have this table in my database:
T_hospital_Clinic:
ID Hospital Clinic Location department
------------------------------------------------------------------
1 KIIMS Hosp. Kar clinic Bhubaneswar Pediatric.
2 SCB medical Aswani clinic Cuttack ENT
3 Sum Mishra clinic Bhubaneswar Orthopedic
4 AMRI Image dignisys Cuttack Neorolgy
5 AIIMS Mahaveer clinic Bhubaneswar ENT
6 shree Hosp shaoo's clinic Cuttack pediatric.
Here the table name is T_hospital_Clinic and I need by putting any location / department name inside search field, I can access the hospital / clinic name.
Suppose I want to check how many hospital / clinics are present in Bhubaneswar those are belongs ENT department and in this case what should be the SQL query to find out the data.
Please help me to write the proper query to solve this problem.
This seems like a very basic sql statement with a where clause that contains 2 conditions on 2 columns joined by an and:
SELECT Hospital, Clinic
FROM T_hospital_Clinic
WHERE Location = #Location -- perhaps it's better to use LIKE here instead
AND department = #department
Do you want to show clinics/hospitals that belongs to a location / department starts with text entered by the user or contains it? Case sensitive/ Case insensitive?
Case sensitive:
For starts case:
SELECT Hospital, Clinic
FROM T_hospital_Clinic
WHERE Location LIKE #location + '%'
AND department LIKE #department + '%'
For contains case:
SELECT Hospital, Clinic
FROM T_hospital_Clinic
WHERE Location LIKE '%' + #location + '%'
AND department LIKE '%' + #department + '%'
Case insensitive:
For starts case:
SELECT Hospital, Clinic
FROM T_hospital_Clinic
WHERE LOWER(Location) LIKE LOWER(#location) + '%'
AND LOWER(department) LIKE LOWER(#department) + '%'
For contains case:
SELECT Hospital, Clinic
FROM T_hospital_Clinic
WHERE LOWER(Location) LIKE '%' + LOWER(#location) + '%'
AND LOWER(department) LIKE '%' + LOWER(#department) + '%'
Where #location and #department exact text entered by user into search fields Department and Location
If it is not a huge amount of data I suggest to return all data from table on client side and then filter them there - it will be faster and reduce load on database
Try this ..
Select Hospital
,Clinic
FROM T_hospital_Clinic
WHERE Location = 'Fill empty string with Hospital Location'
AND department = 'Fill empty string with Department Name'
Here the process is starting from your ui, you have to featch the seach values from the search box in ui and pass that values as a parameter to
a stored procedure and call the stored procedure from your server side code then inside stored procedure write a query to get the details and then display and reload the details with the newly get details.
stored procedure is like
IF EXISTS (
SELECT *
FROM sys.objects
WHERE NAME = 'exampleproc' type = 'P'
)
BEGIN
DROP PROCEDURE exampleproc
END
GO
CREATE PROCEDURE exampleproc #Location NVARCHAR(1000);
#department NVARCHAR(1000);AS
BEGIN
SELECT Hospital
,Clinic
FROM T_hospital_Clinic
WHERE Location = #Location
AND department = #department
END

sql server after insert trigger update 3 fields of inserted row, ONLY for specific users and ONLY if fields are blank

I don't have access to the Insert Statement so if they are blank, I don't know if the blank fields are even part of the Insert statement to begin with. Office users and Field (tablet) users insert Work Order records using different applications. To keep the field users from having to populate their Crew Name, Supervisor's name and Shop Name on every record, I've put them into a lookup table keyed on the INITIATEDBY field from the Work Order record (which is auto populated by the app). Office workers may be creating Work orders for anyone but Field Crews only create work orders for their crews so when a Field crew inserts a record I want to populate the 3 fields for them. (Actually they cannot populate the 3 fields because I have hidden them on the Work Order form they use.)
Your trigger code needs to be a set based approach. In the answer you posted you assume there will only be a single row in inserted. Something like this more along the lines of what you want.
This would be the ENTIRE body of the trigger.
Update w
set Crew = tu.Crew
, SHOP = tu.Shop
, Supervisor = tu.Supervisor
from inserted i
join TableUsers tu on tu.EmpName = i.INITBY
join WorkOrder w on i.ID = w.WOID
I figures it out myself. I just had to read enough examples to put it all together. The only thing that scares me is the IF #EMPName <> ''. Is there a better way to check if a record was retrieved in the 2nd select statement?
CREATE TRIGGER trgUpdateCrewShopSuper ON ESDWO
FOR INSERT
AS
BEGIN
DECLARE #ID nvarchar(60), #InitBy nvarchar(50), #EMPName nvarchar(50), #CREW as nvarchar(50), #Shop nvarchar(100), #Super nvarchar(50);
select #ID=i.ID, #InitBy=i.INITBY from inserted i;
select #EMPName=EmpName, #CREW=CrewName, #Shop=SHOP, #Super=Supervisor
From dbo.TabletUsers
WHERE EmpName = #InitBy
IF #EMPName <> ''
BEGIN
update dbo.WorkOrder
set Crew = #Crew, SHOP = #Shop, Supervisor = #Super
WHERE WOID = #ID
END
END

Data between two dates using where condition

I'am Using a stored procedure to retrieve values between data using Where condition(related to particular Person not the one related to Date).
E.g I have table with cols
User Name User ID date_used Book Name
xxxx UID001 12-03-2012 zzzzz
yyyy UID002 14-03-2015 ccccc
xxxx UID001 14-03-2015 rrrrr
Desired Out put
User Name User ID date_used Book Name
xxxx UID001 12-03-2012 zzzzz
xxxx UID001 14-03-2015 rrrrr
For this I tried below way, please help me where i'm wrong
SELECT UserID,Issue_Date,Book_Name FROM dbo.Books
where UserID = #userID and Issue_Date >= #From and Issue_Date <= #To
order by Issue_Date Desc
#From(datetime) and #to(datetime) are datepickers value(startDate and EndDate)
#userID varchar
When i remove UserID clause I was able to retrive all the books issued for all the users. but when i add a where clause of user id no rows are reflected.
Please advice me on this with other methods to get this done.

Resources