How to fetch records using vertical columns - sql

I need to fetch records between the date range given in two columns.
My Table structure is like:
CREATE TABLE shared
(
id integer NOT NULL,
product varchar(50) NOT NULL,
parent_id integer NOT NULL,
key_code varchar(100) NOT NULL,
key_value varchar(8000)
);
INSERT INTO shared
(`id`, `product`, `parent_id`, 'key_code', 'key_value')
VALUES
(1, 'a',1, 'start_date','1/7/2011'),
(2, 'a', 1,'end_date','15/7/2011'),
(3, 'a',1, 'type','Promotion'),
(4, 'a',1,'plan', 'new'),
(5, 'a',5, 'start_date','11/8/2012'),
(6, 'a', 5,'end_date','15/8/2012'),
(7, 'a',5, 'type','Promotion'),
(8, 'a',5,'plan', 'new'),
(9, 'b',9, 'start_date','15/09/2015'),
(10, 'b', 9,'end_date','15/09/2016'),
(11, 'b',9, 'type','Promotion'),
(12, 'b',9,'plan', 'new'),
;
Now i want to fetch all the records between start date>='1/7/2011' to start_date<='15/8/2012' where product='a'.
The Query I tried is:
SELECT
*
FROM
(SELECT parent_id,
product,
MIN(CASE WHEN key_code = 'key_code' THEN key_value ELSE 'n/a' END) AS key_code,
MIN(CASE WHEN key_code = 'start_date' THEN key_value ELSE 'n/a' END) AS start_date,
MIN(CASE WHEN key_code = 'end_date' THEN key_value ELSE 'n/a' END) AS end_date,
MIN(CASE WHEN key_code = 'type' THEN key_value ELSE 'n/a' END) AS type,
MIN(CASE WHEN key_code = 'plan' THEN key_value ELSE 'n/a' END) AS plan,
FROM shared
GROUP BY parent_id,
product
ORDER BY parent_id) comp
WHERE
start_date>= '01/12/2011'
AND start_date <= '02/17/2011' and product='a';
I am getting records now, changed the date format.
But is there any way to optimize this query.? Like this will take time to execute when records are in numbers.

Your dates have not the MySQL format, so they have to be converted,to be compared
With the MIN and MAX and your 'n/a, it is better to use NULL in the inner Select and in the outer assign the Value n/a if the value is NULL, ot or else you get n/qa even if there is data.
SELECT
*
FROM
(SELECT
parent_id,
product,
MIN(CASE WHEN key_code = 'start_date' THEN key_value ELSE 'n/a' END) AS start_date,
MIN(CASE WHEN key_code = 'end_date' THEN key_value ELSE 'n/a' END) AS end_date,
MAX(CASE WHEN key_code = 'type' THEN key_value ELSE 'n/a' END) AS type,
MAX(CASE WHEN key_code = 'plan' THEN key_value ELSE 'n/a' END) AS plan
FROM
shared
GROUP BY parent_id , product
ORDER BY parent_id) comp
WHERE
TO_DATE(start_date, 'DD/MM/YYYY') >= '2011-01-12'
AND TO_DATE(start_date, 'DD/MM/YYYY') <= '2011-07-17'
AND product = 'a'
;
Thsi would give youz
parent_id product start_date end_date type plan
1 a 1/7/2011 15/7/2011 Promotion new
So STR:TO:DATE doesn't exist in AWS but it EXISTS a TO_Date
Instead of IF you maust do A CASe when like in your query

Related

Why doesn't INTEGER/INTEGER does not give me an answer in SQL?

I am trying to find the success rate of the messages received in SQL. Hence, I create a table with message id and action (sent or received) then I count the no. of received msgs/total no. of messages. Here's my code:
CREATE TABLE msg (id INTEGER, action TEXT);
INSERT INTO msg VALUES (1, 'sent');
INSERT INTO msg VALUES (1, 'received');
INSERT INTO msg VALUES (2, 'sent');
INSERT INTO msg VALUES (2, 'received');
INSERT INTO msg VALUES (3, 'sent');
INSERT INTO msg VALUES (4, 'sent');
INSERT INTO msg VALUES (5, 'sent');
SELECT SUM(CASE WHEN action = 'received' THEN 1 ELSE 0 END)/COUNT( DISTINCT id) AS success_rate FROM msg;
This gives me 0 as output. Why?
Because your database does integer division. Simple add a decimal point:
SELECT SUM(CASE WHEN action = 'received' THEN 1.0 ELSE 0 END) / COUNT(DISTINCT id) AS success_rate
FROM msg;
I might want to divide by the number sent -- just in case:
SELECT (SUM(CASE WHEN action = 'received' THEN 1.0 ELSE 0 END) /
SUM(CASE WHEN action = 'sent' THEN 1.0 END)
) AS success_rate
FROM msg;
Or by unique accounts in both cases:
SELECT (COUNT(DISTINCT CASE WHEN action = 'received' THEN id END) /
COUNT(DISTINCT id)
) AS success_rate
FROM msg;
If id were unique (which seems reasonable to me), this can be simplified to:
SELECT AVG(CASE WHEN action = 'received' THEN 1.0 ELSE 0 END) AS success_rate
FROM msg;

Creating new columns in Sql Server based on other columns

I have a table T :
CREATE TABLE T
(
id INT,
type VARCHAR(200),
type_value VARCHAR(10),
value VARCHAR(200)
);
INSERT INTO T VALUES (1, 'HomePhone', 'p1', '1234 ');
INSERT INTO T VALUES (1, 'HomePhone', 'p2', '5678 ');
INSERT INTO T VALUES (1, 'HomePhone', 'p3', '4567');
INSERT INTO T VALUES (1, 'WorkPhone', 'w1', '9007 ');
INSERT INTO T VALUES (2, 'Email', 'e1', 'abc#xyz.com ');
INSERT INTO T VALUES (2, 'Email', 'e1', 'efg#xyz.com');
INSERT INTO T VALUES (2, 'Email', 'e2', 'mno#xyz.com');
INSERT INTO T VALUES (3, 'WorkPhone', 'w1', '0100');
INSERT INTO T VALUES (3, 'WorkPhone', 'w2', '0110');
INSERT INTO T VALUES (4, 'OtherPhone', 'o1', '1010 ');
INSERT INTO T VALUES (4, 'OtherPhone', 'o1', '1110 ');
INSERT INTO T VALUES (4, 'OtherPhone', 'o1', '1011');
INSERT INTO T VALUES (4, 'HomePhone', 'p1', '2567 ');
I need to transform it into :
id primaryhomephone secondaryhomephone primaryemail secondaryemail Primaryworkphone secondaryworkphone primaryotherphone secondaryotherphone
----------------------------------------------------------------------------------------------------------------------------------------------------------------
1 1234 5678 null null 9007 null null null
2 null null abc#xyz.com efg#xyz.com null null null null
3 null null null null 0100 0110 null null
4 2567 null null null null null 1010 1011
Basically the field will be divided based on type_value. If there is two type_value for same id and type then first type will be primary and second type will be secondary.
For more than two type values for same id and type, discard the third one.
For more than two type values of same type(for example o1,o1) for id 4 first o1 will be primaryotherphone and second one will be secondaryprimaryphone.
Sorry if this question has been repeated before but somehow I can't solve it. Can anyone please help. Thanks a lot
You can use MAX/GROUP BY technique. PIVOT is quite complex for beginners, but I agree that the purpose of the PIVOT is the exactly what you need:
SELECT id
, MAX(CASE WHEN type_value = 'p1' THEN value END ) AS primaryhomephone
, MAX(CASE WHEN type_value = 'p2' THEN value END ) AS secondaryhomephone
, MAX(CASE WHEN type_value = 'p3' THEN value END ) AS thirdphone
, MAX(CASE WHEN type_value = 'w1' THEN value END ) AS workphone
, MAX(CASE WHEN type_value = 'w2' THEN value END ) AS secondaryworkphone
, MAX(CASE WHEN type_value = 'o2' THEN value END ) AS otherworkphone
, MAX(CASE WHEN type_value = 'e1' THEN value END ) AS primaryemail
, MAX(CASE WHEN type_value = 'e2' THEN value END ) AS secondaryemail
FROM T
GROUP BY id
;
You need to look after computed columns in SQL Server.
CREATE TABLE [dbo].T
(
...
[primaryhomephone] AS (CASE WHEN type_value = 'p1' THEN value ELSE NULL END)
)
Alternatively you can use Views:
CREATE VIEW dbo.vwT
AS
SELECT
id
,(CASE WHEN type_value = 'p1' THEN value ELSE NULL END as [primaryhomephone]
, (CASE WHEN type_value = 'p2' THEN value ELSE NULL END as [secondaryhomephone]
FROM dbo.T
You need to look into PIVOT Column presentation. You can use it in to ways, you can create new Table with your column and then use PIVOT Query to insert them there, or you can simply fetch at runtime. If there is lot of data and all other Programming interface are ready to upgrade then make new table, else just use query.
Here is the link for your reference: https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx

Using SQL CASE Statement how to find multiple items

I need to get a value for “THEN” from “Mortgage_Type” column if bellow conditions are true. Mortgage_Type and Category are same table and Equipment from another table. Tables are joining using Item_No. I need to find the Mortgage_Type of each item. I have 20+ Mortgage_Types if Category is main and Equipment is null then should display relevant Mortgage_Type
when SUM (Mortgage_Type)is not null and SUM (Equipment)is null and sum(Category) =’M’ THEN “value from Mortgage_Type column”
Maybe, this should help:
SELECT DISTINCT
Contract,
CASE
WHEN CATOGORY NOT IN ('M', 'O') AND TYPE NOT IN ('V', 'E')
THEN
CASE
WHEN M_TYPE = 'V' THEN 'VEHICLE'
WHEN M_TYPE = 'O' THEN 'OTHERS'
WHEN M_TYPE = 'M' THEN 'MORTGAGE'
WHEN M_TYPE = 'H' THEN 'HOUSE'
WHEN M_TYPE LIKE '%,%' THEN 'MULTIPLE'
END
END
AS TYPE
FROM Table1 LEFT JOIN Table2 ON Contract = ID
You could use a cte and a window function. Something like this:
DECLARE #t TABLE(
[Contract] int
,[M_TYPE] nvarchar(100)
)
INSERT INTO #t VALUES
(1, 'V')
,(1, 'O')
,(1, 'M')
,(2, 'V')
,(3, 'V')
,(4, 'H')
,(4, 'V');
WITH cte AS(
SELECT t.Contract,
t.M_TYPE,
COUNT(M_TYPE) OVER (PARTITION BY t.Contract) Multi
FROM #t t
)
SELECT DISTINCT [Contract], CASE
WHEN Multi > 1 THEN 'Multiple'
WHEN M_TYPE = 'V' THEN 'VEHICLE'
WHEN M_TYPE = 'O' THEN 'OTHERS'
WHEN M_TYPE = 'M' THEN 'MORTGAGE'
WHEN M_TYPE = 'H' THEN 'HOUSE'
ELSE 'UNKNOWN'
END AS MortgageType
FROM cte

Find order by based on column value not column name

I have a query where I want to order based on column value not column name i.e.
Suppose I have a column "Description" in table and in my sql I have like condition as
Select * from tableName
where Description like '%Bombay%'
or Description like '%Hotel%' like Description like '%Rent%'
I want to order the records if column Description have all the three values or two values or one value i.e. if all the three values are in Description column it should be in at top
Is it possible in sql ?
drop table if exists dbo.tableName;
create table dbo.tableName (
ID int primary key
, Description varchar(100)
);
insert into dbo.tableName (ID, Description)
values (1, 'Hotel Bombay Rent')
, (2, 'Hotel Bombay')
, (3, 'Hotel')
, (4, 'Aparment');
select
t.ID
, t.Description
from dbo.tableName t
order by (case when t.Description like '%Hotel%' then 1 else 0 end)
+ (case when t.Description like '%Bombay%' then 1 else 0 end)
+ (case when t.Description like '%Rent%' then 1 else 0 end) desc

Return SQL results back as a single row for case scenarios

So I have the following SQL query:
SELECT CASE
WHEN STATUS = 0 THEN 'Pending'
WHEN STATUS = 1 THEN 'Pending'
WHEN STATUS = 2 THEN 'Confirmed'
WHEN STATUS = 3 THEN 'Pending'
WHEN STATUS = 4 THEN 'Pending'
WHEN STATUS = 5 THEN 'Pending'
WHEN STATUS = 6 THEN 'Paid'
WHEN STATUS = 7 THEN 'Closed'
WHEN STATUS = 8 THEN 'Rejected' END AS Statuses
FROM ORDERS
WHERE opportunityid = 4054
GROUP BY Statuses
Which returns the following results:
Pending
Confirmed
However, I am trying to achieve the following:
Pending,Confirmed
Can this be done without the use of a #declare function too.
I think you want something like this:
SELECT STUFF(MAX(CASE WHEN STATUS IN (0, 3, 4, 5) THEN ',Pending' ELSE '' END) +
MAX(CASE WHEN STATUS IN (2) THEN ',Confirmed' ELSE '' END) +
MAX(CASE WHEN STATUS IN (6) THEN ',Paid' ELSE '' END) +
MAX(CASE WHEN STATUS IN (7) THEN ',Closed' ELSE '' END) +
MAX(CASE WHEN STATUS IN (8) THEN ',Rejected' ELSE '' END),
1, 1, ''
) as statuses
FROM ORDERS
WHERE opportunityid = 4054;
This will for status which are not predefined but exists in a table:
DECLARE #t table(STATUSES INT,opportunityid INT)
INSERT #t values(1,4054),(2,4054),(3,4054)
INSERT #t values(1,4055),(6,4055),(7,4055),(12,4055)
DECLARE #t2 table(sta int, txt varchar(30))
INSERT #t2
values
(0, 'Pending'),
(1, 'Pending'),
(2, 'Confirmed'),
(3, 'Pending'),
(4, 'Pending'),
(5, 'Pending'),
(6, 'Paid'),
(7, 'Closed'),
(8, 'Rejected')
;WITH CTE as
(
SELECT distinct opportunityid FROM #t
)
SELECT
t.*,
STUFF((
SELECT distinct ',' + coalesce([txt], 'Unknown')
FROM #t t1
LEFT JOIN
#t2 x
ON x.sta = t1.STATUSES
WHERE t1.opportunityid = t.opportunityid
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, '') [status]
FROM cte t