Add additional column to query based on dates in another table - sql-server-2008

I'm looking to query the datatbl table and return the table with an additional column based on the Start_date values of the productstbl table.
Below I have created a schema with the initial table (datatbl_before) and the desired end result (datatbl_after), where a Product_Lookup column has been added to the datatbl table and shows the corresponding Product from the productstbl based on the Asset_ID of the line and the Start_date.
For example, in the first row of datatbl, the row relates to Asset_ID '1' and occurred on 2017-08-16, this would retrieve the product value 'A' from the productstbl table as it is after the start_date value 2017-08-12, but before the next start_date value of 2017-09-27 related to that Asset_ID .
In the case where there is no proceeding date for the Asset_ID lookup and start_date, a 'NONE' value could be returned - e.g. Row 5 of datatbl
Can anyone provide any direction or advice on how to tackle this one?
Many thanks in advance!
CREATE TABLE datatbl_before (
PDate DATE,
Asset_ID varchar(255),
Rev int
);
CREATE TABLE datatbl_after (
PDate DATE,
Asset_ID varchar(255),
Rev int,
Product_Lookup varchar(255)
);
CREATE TABLE productstbl (
Asset_ID varchar(255),
Start_date DATE,
Product varchar(255)
);
INSERT INTO productstbl
VALUES
(' 1 ',' 2017-08-12' ,' A' ),
(' 1' ,' 2017-09-27' ,' B' ),
(' 1' ,' 2018-02-14' ,' C' ),
(' 2' ,' 2018-01-17' ,' A' ),
(' 2' ,' 2018-02-21' ,' B' ),
(' 2' ,' 2018-03-05' ,' C' );
INSERT INTO datatbl_before
VALUES
(' 2017-08-16' ,' 1' ,' 10'),
(' 2017-09-29' ,' 1' ,' 15'),
(' 2018-02-13' ,' 1' ,' 12'),
(' 2018-02-19' ,' 1' ,' 10'),
(' 2018-01-01' ,' 2' ,' 12'),
(' 2018-01-25' ,' 2' ,' 33'),
(' 2018-02-25' ,' 2' ,' 67'),
(' 2018-03-07' ,' 2' ,' 71');
INSERT INTO datatbl_after
VALUES
(' 2017-08-16' ,' 1' ,' 10' ,' A' ),
(' 2017-09-29' ,' 1' ,' 15' ,' B' ),
(' 2018-02-13' ,' 1' ,' 12' ,' B' ),
(' 2018-02-19' ,' 1' ,' 10' ,' C' ),
(' 2018-01-01' ,' 2' ,' 12' ,' NONE'),
(' 2018-01-25' ,' 2' ,' 33' ,' A' ),
(' 2018-02-25' ,' 2' ,' 67' ,' B' ),
(' 2018-03-07' ,' 2' ,' 71' ,' C' );

You can use a sub query, which goes and selects the data you want, using data from the outer query in the where clause. In this case, it finds all the products that are the right asset, and started before or on the transaction date, sorts them so the most recent one is first (the one we want) and takes only that one (The TOP 1 part).
The ISNULL part changes any missing values from NULL to 'NONE'
Select DB.*,
ISNULL(
(Select top 1 product
from productstbl
where Asset_ID = DB.Asset_ID
and Start_date <= DB.PDate
order by Start_date desc
)
, 'NONE') as Product_Lookup
from datatbl_before DB;

Related

Best way to ignore null values in MYSQL

My data looks like this,
I have multiple values in the Table field with each having its own unique values for the Data field. However, Data values for TableA will be the same format in the table myTable. Same applies to all records in the Table field.
I am using JSON_EXTRACT to get the value in conjunction with CASE statements.
SELECT
CASE
WHEN
Table = 'table A'
THEN
CONCAT_WS('\n','Name: ',
JSON_EXTRACT(Data, '$.name'))
END AS 'tableA',
CASE
WHEN
Table = 'table B'
THEN
CONCAT_WS('\n','Location: ',
JSON_EXTRACT(Data, '$.fieldType'))
END AS 'tableB' from myTable
The problem with this is that I will be getting null values.
I am getting the below results,
I am expecting the below results,
I want to avoid the null values. Is there any other way to extract the data ? I am using MYSQL.
Option one:
Select *
from ( SELECT CASE WHEN Table_c = 'table A' THEN
CONCAT_WS('\n','Name: ', JSON_EXTRACT(data_c, '$.name'))
END AS 'TableA'
from test_t) A,
( SELECT CASE WHEN Table_c = 'table B' THEN
CONCAT_WS('\n','Location: ', JSON_EXTRACT(data_c, '$.fieldType'))
END AS 'TableB'
from test_t) B
where TableA is not null
and TableB is not null
Option two:
SELECT CASE WHEN Table_c = 'table B' THEN
CONCAT_WS('\n','Location: ', JSON_EXTRACT(data_c, '$.fieldType'))
ELSE
CONCAT_WS('\n','Name: ', JSON_EXTRACT(data_c, '$.name'))
END AS 'Data'
, CASE WHEN Table_c = 'table B' THEN
'Table B'
ELSE
'Table A'
END AS 'Table'
from test_t
Here is the demo
Please note that I have changed the names of the table and columns in my demo.
I think you just want conditional aggregation:
SELECT MAX(CASE WHEN Table = 'table A'
THEN CONCAT_WS('\n', 'Name: ', JSON_EXTRACT(Data, '$.name'))
END) AS tableA,
MAX(CASE WHEN Table = 'table B'
THEN CONCAT_WS('\n','Location: ', JSON_EXTRACT(Data, '$.fieldType'))
END) AS tableB
from myTable;
If there could be multiple matches in the JSON, then you might want GROUP_CONCAT() instead:
SELECT GROUP_CONCAT(CASE WHEN Table = 'table A'
THEN CONCAT_WS('\n', 'Name: ', JSON_EXTRACT(Data, '$.name'))
END) AS tableA,
GROUP_CONCAT(CASE WHEN Table = 'table B'
THEN CONCAT_WS('\n','Location: ', JSON_EXTRACT(Data, '$.fieldType'))
END) AS tableB
from myTable;
Note: you have to understand these are not actual null values in database but because your requirement is to read and convert the rows to columns and thus each row in the final result will have null value for all columns other than the corresponding derived one. We can use max on each column (each "CASE" as in your query).
You could try with below,
SELECT MAX(IF(data_type = 'TableA', CONCAT_WS('\n', 'Name: ',
JSON_EXTRACT(data, '$.name'))
, ''))
TableA,
MAX(IF(data_type = 'TableB', CONCAT_WS('\n', 'Location: ',
JSON_EXTRACT(data, '$.fieldType')), ''))
TableB
FROM mytable

return null in mysql procedure with dynamic query

can someone please tell me what I am missing I have invested 4 hours and watch lots of article can't figure out why is showing return null
mysql mysql procedure is as follows.
create procedure proc_studymaterial_listing(
get_exam_id int(11)
)
begin
set #query1 = concat( "select c.content_type,
c.title,
c.status,
c.created_on,
c.pdf_is_downloadable,
a.content_id,
a.expiry_date,
exam_Name,
fun_subject_name(a.subject_id) as subject_name,
fun_subject_name(a.chapter_id) as chapter_name
from tbl_studymaterial_exam_chapter_map as a
join tbl_exam_master as b on a.exam_id= b.exam_ID
join tbl_studymaterial as c on c.id= a.content_id
where c.status=1 " );
if( get_exam_id = 0 )
then
set #exam_id = " and 1=1 ";
else
set #exam_id = concat( ' and b.exam_ID = " ', #get_exam_id , '"' );
end if;
SET #final_query = CONCAT ( #query1,#exam_id );
PREPARE stmt FROM #final_query ;
# select stmt;
# EXECUTE stmt;
# DEALLOCATE PREPARE stmt;
end;
User-defined variable #get_exam_id doesn't appear to be assigned a value anywhere. If that's not assigned a non-NULL value, then the result of evaluating this line:
set #exam_id = concat( ' and b.exam_ID = " ', #get_exam_id , '"' );
is that #exam_id will be NULL. And that will cascade into the next CONCAT, which will also evaluate to NULL.
User-defined variables start with an # at sign character.
Those are distinct and separate from procedure arguments and variables, which do not start with an # at sign character.
Looks like maybe the intent was to reference the local variable get_exam_id, not a user-defined variable.
FOLLOWUP
Personally, if I had to write the body of the procedure, I'd do it something like this:
DELIMITER $$
CREATE PROCEDURE proc_studymaterial_listing( get_exam_id INT(11))
BEGIN
SET #sql = CONCAT( 'SELECT c.content_type'
,'\n' ,' , c.title'
,'\n' ,' , c.status'
,'\n' ,' , c.created_on'
,'\n' ,' , c.pdf_is_downloadable'
,'\n' ,' , a.content_id'
,'\n' ,' , a.expiry_date'
,'\n' ,' , b.exam_name'
,'\n' ,' , fun_subject_name(a.subject_id) AS subject_name'
,'\n' ,' , fun_subject_name(a.chapter_id) AS chapter_name'
,'\n' ,' FROM tbl_studymaterial_exam_chapter_map a'
,'\n' ,' JOIN tbl_exam_master b'
,'\n' ,' ON b.exam_id = a.exam_id'
,'\n' ,' JOIN tbl_studymaterial c'
,'\n' ,' ON c.id = a.content_id'
,'\n' ,' WHERE c.status = 1'
,'\n'
,CASE
WHEN get_exam_id <> 0
THEN ' AND b.exam_id = ? '
ELSE ''
END
,'\n' ,' ORDER BY subject_name, chapter_name, exam_name'
);
PREPARE stmt FROM #sql;
IF (get_exam_id <> 0) THEN
SET #id = get_exam_id;
EXECUTE stmt USING #id;
SET #id = '';
ELSE
EXECUTE stmt;
END IF;
DEALLOCATE PREPARE stmt;
SET #sql = '';
END;
$$
DELIMITER ;

How to avoid showing multiple row? Mysql

SELECT
`BillNum`
, GROUP_CONCAT(CONCAT( `City` , ' / ' , `Region` , ' / ' , `StreetName` , ' / ' , `Closepoint`)) AS 'Address'
, GROUP_CONCAT(CONCAT('(', `ItemCount` ,')', `ItemName` , ' ' )separator ' ') AS OrderInfo
, CustomerPhone , `OrderTime`
FROM orderin
INNER JOIN `customers`
WHERE `CustomerPhone` = `customers`.`PhoneNumber`
AND `TableNum` = '0' AND DeleveryState = '0'
GROUP BY `BillNum`
ORDER BY `BillNum`;
Where is this wrong ? Why the address is multiplied ?
Add Distinct to your Group_Concat
Like
SELECT
`BillNum`
, GROUP_CONCAT(DISTINCT CONCAT( `City` , ' / ' , `Region` , ' / ' , `StreetName` , ' / ' , `Closepoint`)) AS 'Address'
, GROUP_CONCAT(DISTINCT CONCAT('(', `ItemCount` ,')', `ItemName` , ' ' )separator ' ') AS OrderInfo
, CustomerPhone , `OrderTime`
FROM orderin INNER JOIN `customers`
WHERE `CustomerPhone` = `customers`.`PhoneNumber`
AND `TableNum` = '0' AND DeleveryState = '0'
GROUP BY `BillNum`
ORDER BY `BillNum`

Get parent column in 3rd-level subquery

How can I get a column from the top-most parent query in a subquery within a subquery? Do I have to pass it like a variable? Here's my code:
SELECT c.in_customer_id,
(
SELECT
group_concat(the_schedule separator '\r\n')
FROM
(
SELECT
concat_ws('\n', 'Route: ', s.route_id, 'Interval: ', s.service_interval, 'Week No.: ', s.week_no, 'Weekdays: ', s.weekdays, 'Start Date: ', s.start_date, 'End Date: ', s.end_date, 'Start Time: ', s.start_time, 'End Time: ', s.end_time, '\n') AS the_schedule
FROM
schedule s
WHERE
s.service_address_id IN
(
SELECT in_customer_address_id
FROM tbl_customer_address a2
WHERE a2.in_customer_id = c.in_customer_id
)
AND s.is_skipped = '0'
GROUP BY
s.service_address_id
) a
)
AS "Schedule"
FROM
tbl_customers c
The response I get is "Error Code: 1054. Unknown column 'c.in_customer_id' in 'where clause'"
As a principle, you want to move the sub-queries in to your FROM clause.
Try something like this...
SELECT
c.in_customer_id,
s.grouped_schedule
FROM
tbl_customers AS c
LEFT JOIN
(
SELECT
in_customer_id,
group_concat(the_schedule separator '\r\n') AS grouped_schedule
FROM
(
SELECT
a.in_customer_id,
a.in_customer_address_id,
concat_ws('\n', 'Route: ', s.route_id, 'Interval: ', s.service_interval, 'Week No.: ', s.week_no, 'Weekdays: ', s.weekdays, 'Start Date: ', s.start_date, 'End Date: ', s.end_date, 'Start Time: ', s.start_time, 'End Time: ', s.end_time, '\n') AS the_schedule
FROM
tbl_customer_address AS a
INNER JOIN
schedule AS s
ON s.service_address_id = a.in_customer_address_id
WHERE
s.is_skipped = 0
)
AS schedules
GROUP BY
in_cusomer_id
)
AS s
ON s.in_customer_id = c.in_customer_id

T-SQL substring - separating first and last name

I have a column which has FirstName and LastName together. I'm writing a report to separate the FirstName And LastName. How do I get the FirstName and LastName separated in T-SQL?
Assuming the FirstName is all of the characters up to the first space:
SELECT
SUBSTRING(username, 1, CHARINDEX(' ', username) - 1) AS FirstName,
SUBSTRING(username, CHARINDEX(' ', username) + 1, LEN(username)) AS LastName
FROM
whereever
The easiest way I can find to do it is:
SELECT
SUBSTRING(FullName, 1, CHARINDEX(' ', FullName) - 1) AS FirstName,
REVERSE(SUBSTRING(REVERSE(FullName), 1, CHARINDEX(' ', REVERSE(FullName)) - 1)) AS LastName
FROM
[PERSON_TABLE]
This should work:
Select
LTRIM(RTRIM(SUBSTRING(FullName, 0, CHARINDEX(' ', FullName)))) As FirstName
, LTRIM(RTRIM(SUBSTRING(FullName, CHARINDEX(' ', FullName)+1, 8000)))As LastName
FROM TABLE
Edit: Adopted Aaron's and Jonny's hint with the fixed length of 8000 to avoid unnecessary calculations.
validate last name is blank
SELECT
person.fullName,
(CASE WHEN 0 = CHARINDEX(' ', person.fullName)
then person.fullName
ELSE SUBSTRING(person.fullName, 1, CHARINDEX(' ', person.fullName)) end) as first_name,
(CASE WHEN 0 = CHARINDEX(' ', person.fullName)
THEN ''
ELSE SUBSTRING(person.fullName,CHARINDEX(' ', person.fullName), LEN(person.fullName) )end) last_name
FROM person
Here is a more elaborated solution with a SQL function:
GetFirstname
CREATE FUNCTION [dbo].[ufn_GetFirstName]
(
#FullName varchar(500)
)
RETURNS varchar(500)
AS
BEGIN
-- Declare the return variable here
DECLARE #RetName varchar(500)
SET #FullName = replace( replace( replace( replace( #FullName, '.', '' ), 'Mrs', '' ), 'Ms', '' ), 'Mr', '' )
SELECT
#RetName =
CASE WHEN charindex( ' ', ltrim( rtrim( #FullName ) ) ) > 0 THEN left( ltrim( rtrim( #FullName ) ), charindex( ' ', ltrim( rtrim( #FullName ) ) ) - 1 ) ELSE '' END
RETURN #RetName
END
GetLastName
CREATE FUNCTION [dbo].[ufn_GetLastName]
(
#FullName varchar(500)
)
RETURNS varchar(500)
AS
BEGIN
DECLARE #RetName varchar(500)
IF(right(ltrim(rtrim(#FullName)), 2) <> ' I')
BEGIN
set #RetName = left(
CASE WHEN
charindex( ' ', reverse( ltrim( rtrim(
replace( replace( replace( replace( replace( replace( #FullName, ' Jr', '' ), ' III', '' ), ' II', '' ), ' Jr.', '' ), ' Sr', ''), 'Sr.', '')
) ) ) ) > 0
THEN
right( ltrim( rtrim(
replace( replace( replace( replace( replace( replace( #FullName, ' Jr', '' ), ' III', '' ), ' II', '' ), ' Jr.', '' ), ' Sr', ''), 'Sr.', '')
) ) , charindex( ' ', reverse( ltrim( rtrim(
replace( replace( replace( replace( replace( replace( #FullName, ' Jr', '' ), ' III', '' ), ' II', '' ), ' Jr.', '' ), ' Sr', ''), 'Sr.', '')
) ) ) ) - 1 )
ELSE '' END
, 25 )
END
ELSE
BEGIN
SET #RetName = left(
CASE WHEN
charindex( ' ', reverse( ltrim( rtrim(
replace( replace( replace( replace( replace( replace( replace( #FullName, ' Jr', '' ), ' III', '' ), ' II', '' ), ' I', '' ), ' Jr.', '' ), ' Sr', ''), 'Sr.', '')
) ) ) ) > 0
THEN
right( ltrim( rtrim(
replace( replace( replace( replace( replace( replace( replace( #FullName, ' Jr', '' ), ' III', '' ), ' II', '' ), ' I', '' ), ' Jr.', '' ), ' Sr', ''), 'Sr.', '')
) ) , charindex( ' ', reverse( ltrim( rtrim(
replace( replace( replace( replace( replace( replace( replace( #FullName, ' Jr', '' ), ' III', '' ), ' II', '' ), ' I', '' ), ' Jr.', '' ), ' Sr', ''), 'Sr.', '')
) ) ) ) - 1 )
ELSE '' END
, 25 )
END
RETURN #RetName
END
USE:
SELECT dbo.ufn_GetFirstName(Fullname) as FirstName, dbo.ufn_GetLastName(Fullname) as LastName FROM #Names
You could do this if firstname and surname are separated by space:
SELECT SUBSTRING(FirstAndSurnameCol, 0, CHARINDEX(' ', FirstAndSurnameCol)) Firstname,
SUBSTRING(FirstAndSurnameCol, CHARINDEX(' ', FirstAndSurnameCol)+1, LEN(FirstAndSurnameCol)) Surname FROM ...
This will take care of names like "Firstname Z. Lastname" and "First Z Last"
SELECT
CASE
WHEN CHARINDEX(' ',name) = 0 THEN name
WHEN CHARINDEX(' ',name) = PATINDEX('% _[., ]%',name) THEN RTRIM(SUBSTRING(name, 1, CHARINDEX(' ',name) + 2))
ELSE SUBSTRING(name,1, CHARINDEX(' ',name))
END [firstname]
,CASE
WHEN CHARINDEX(' ',name) = 0 THEN ''
WHEN CHARINDEX(' ',name) = PATINDEX('% _[., ]%',name) THEN LTRIM(SUBSTRING(name, CHARINDEX(' ',name) + 3,1000))
ELSE SUBSTRING(name,CHARINDEX(' ',name)+1,1000)
END [lastname]
FROM [myTable]
I think below query will be helpful to split FirstName and LastName from FullName even if there is only FirstName.
For example: 'Philip John' can be split into Philip and John.
But if there is only Philip, because of the charIndex of Space is 0, it will only give you ''.
Try the below one.
declare #FullName varchar(100)='Philp John'
Select
LTRIM(RTRIM(SUBSTRING(#FullName, 0, CHARINDEX(' ', #FullName+' ')))) As FirstName
, LTRIM(RTRIM(SUBSTRING(#FullName, CHARINDEX(' ', #FullName+' ')+1, 8000)))As LastName
Hope this will help you. :)
You may have problems if the Fullname doesn't contain a space.
Assuming the whole of FullName goes to Surname if there is no space and FirstName becomes an empty string, then you can use this:
SELECT
RTRIM(LEFT(FullName, CHARINDEX(' ', FullName))) AS FirstName,
SUBSTRING(FullName, CHARINDEX(' ', FullName) + 1, 8000) AS LastName
FROM
MyNameTable;
The code below works with Last, First M name strings. Substitute "Name" with your name string column name. Since you have a period as a final character when there is a middle initial, you would replace the 2's with 3's in each of the lines (2, 6, and 8)- and change "RIGHT(Name, 1)" to "RIGHT(Name, 2)" in line 8.
SELECT SUBSTRING(Name, 1, CHARINDEX(',', Name) - 1) LastName ,
CASE WHEN LEFT(RIGHT(Name, 2), 1) <> ' '
THEN LTRIM(SUBSTRING(Name, CHARINDEX(',', Name) + 1, 99))
ELSE LEFT(LTRIM(SUBSTRING(Name, CHARINDEX(',', Name) + 1, 99)),
LEN(LTRIM(SUBSTRING(Name, CHARINDEX(',', Name) + 1, 99)))
- 2)
END FirstName ,
CASE WHEN LEFT(RIGHT(Name, 2), 1) = ' ' THEN RIGHT(Name, 1)
ELSE NULL
END MiddleName
For the last name as in US standards (i.e., last word in the [Full Name] column) and considering first name to include a possible middle initial, middle name, etc.:
SELECT DISTINCT
[Full Name]
,REVERSE([Full Name]) -- to visualize what the formula is doing
,CHARINDEX(' ', REVERSE([Full Name])) -- finds the last space in the string
,[Last Name] = REVERSE(RTRIM(LTRIM(LEFT(REVERSE([Full Name]), CHARINDEX(' ', REVERSE([Full Name]))))))
,[First Name] = RTRIM(LTRIM(LEFT([Full Name], LEN([Full Name]) - CHARINDEX(' ', REVERSE([Full Name])))))
FROM ...
Note that this assumes [Full Name] has no spaces before or after the actual string. Otherwise, use RTRIM and LTRIM to remove these.
Let's suppose your table has Name column and it contains data like -
Random Person
FIRST LAST
Alpha Beta
The query will be like this
SELECT
LEFT(NAME,CHARINDEX(' ',NAME)) AS "First Name",
LTRIM(RTRIM(SUBSTRING(NAME,CHARINDEX(' ',NAME),100))) AS "Last Name"
FROM YOUR_TABLE