I really want to why my index not working.
I have two table post, post_log.
create table post
(
id int auto_increment
primary key,
comment int null,
is_used tinyint(1) default 1 not null,
is_deleted tinyint(1) default 0 not null
);
create table post_log
(
id int auto_increment
primary key,
post_id int not null,
created_at datetime not null,
user int null,
constraint post_log_post_id_fk
foreign key (post_id) references post (id)
);
create index post_log_created_at_index
on post_log (created_at);
When I queried below, created_at index works well.
explain
SELECT *
FROM post p
INNER JOIN post_log pl ON p.id = pl.post_id
WHERE pl.created_at > DATE('2022-06-01')
AND pl.created_at < DATE('2022-06-08')
AND p.is_used is TRUE
AND p.is_deleted is FALSE;
When I queried below, it doesn't work and post table do full scan.
explain
SELECT *
FROM post p
INNER JOIN post_log pl ON p.id = pl.post_id
WHERE pl.created_at > DATE('2022-06-01')
AND pl.created_at < DATE('2022-06-08')
AND p.is_used = 1
AND p.is_deleted = 0;
And below not working either.
explain
SELECT *
FROM post p
INNER JOIN post_log pl ON p.id = pl.post_id
WHERE pl.created_at > DATE('2022-06-01')
AND pl.created_at < DATE('2022-06-08')
and p.comment = 111
what is different between 'tinyint = 1' and 'tinyint is true'?
and, why first query work correctly and the others don't work correctly??
When making the query plan, MySQL has to decide whether to first filter the post_log table using the index, or first filter the post table using the is_used and is_deleted columns.
= 1 tests for the specific value 1, while IS TRUE is true for any non-zero value. I guess it decides that when you're searching for specific values, it will be more efficient to filter the post table first because there will likely be fewer matches (since these columns aren't indexed, it doesn't know that 0 and 1 are the only values).
I have a table that has the following fields:
| entity_id | parent_entity_id | name | status |
|----------------------------------------------|
I'm attempting to write a query that displays every entity that doesn't have a parent and displays their children's name and status inline for a result like this:
| entity_id | name | child_entity_1_name | child_entity_1_status |...| child_entity_4_name | child_entity_4_status |
--------------------------------------------------------------------------------------------------------------------
I know the data is structured so that every entity has at least 3 children, but not every entity has 4 (therefore the ones with 3 will have NULL in the columns for the 4th child name and status). Furthermore, I know that no entity that has a parent is a parent itself.
From the introductory database classes I've taken, this seems like a complicated query. The part that's tripping me up is getting all of the sub-entities into the same row. I can get one sub-entity in the same row as its parent but can't get more than one.
EDIT: The database is basically a set of trees each with a height of 2. There are no grandparents.
PARENT_ENT_1 PARENT_ENT_2
| | | | | | | |
| | | | | | | |
C1 C2 C3 C4 C5 C6 C7 C8
Every row in my result query should represent one of these trees
This works: http://sqlfiddle.com/#!9/e1127f/27/0
But I feel like it should be much, much easier.
I basically had this:
SELECT P.entity_id as Parent_id, P.name as Parent_Name, C1.entity_id, C1.Name,
C2.entity_id, C2.Name, C3.entity_id, C4.Name, C4.entity_id, C4.Name
FROM entity P
JOIN entity C1 on C1.parent_entity_id = P.entity_id
JOIN entity C2 on C2.parent_entity_id = P.entity_id
JOIN entity C3 on C3.parent_entity_id = P.entity_id
LEFT JOIN entity C4 on C4.parent_entity_id = P.entity_id
WHERE P.parent_entity_id IS NULL
AND C1.entity_id < C2.entity_id
AND C2.entity_id < C3.entity_id
AND C3.entity_id < C4.entity_id
But of course that final join won't work as it is there, because the WHERE clause turns it into an INNER join.. Maybe someone will see an easy way to handle that part.
I ended up relenting and using a UNION, one half for parents with 3 children and the other for parents with 4.
Edit: Thank you Paul for making the final join work!
SELECT P.entity_id as Parent_id, P.name as Parent_Name, C1.entity_id, c1.Name,
C2.entity_id, c2.Name, C3.entity_id, c3.Name, C4.entity_id, c4.Name
FROM entity P
JOIN entity C1 on C1.parent_entity_id = P.entity_id
JOIN entity C2 on C2.parent_entity_id = P.entity_id
JOIN entity C3 on C3.parent_entity_id = P.entity_id
LEFT JOIN entity C4 on C4.parent_entity_id = P.entity_id
and c3.entity_id < c4.entity_id
WHERE p.parent_entity_id IS NULL
AND C1.entity_id < C2.entity_id
AND C2.entity_id < C3.entity_id
AND (3 = (SELECT COUNT(1)
FROM entity c
WHERE c.parent_entity_id = p.entity_id)
OR c4.entity_id is not null)
Here is a query for two children:
select
p.entity_id, p.name,
c1.name as child_entity_1_name,
c1.status as child_entity_1_status,
c2.name as child_entity_2_name,
c2.status as child_entity_2_status
from entities p
left join entities c1 on c1.entity_id = (
select c.entity_id
from entities c
where c.parent_entity_id = p.entity_id
order by c.entity_id asc
limit 1
offset 0
)
left join entities c2 on c2.entity_id = (
select c.entity_id
from entities c
where c.parent_entity_id = p.entity_id
order by c.entity_id asc
limit 1
offset 1
)
where p.parent_entity_id is null
For child_entity_3 you will use offset 2 and for child_entity_4 you will use offset 3.
But I would rather just use the following two queries
select p.entity_id, p.name
from entities p
where p.parent_entity_id is null;
select p.entity_id as parent_id, c.name, c.status
from entities p
join entities c on c.parent_entity_id = p.entity_id
where p.parent_entity_id is null
order by p.entity_id, c.entity_id;
and create the desired table in application language with a couple of simple loops.
SET #cNum := 0;
SET #prevParent := 0;
SELECT p.id, p.Name
, GROUP_CONCAT(IF(numberedChildren.childNum = 1, c.Name, NULL)) AS child_entity_1_name
, GROUP_CONCAT(IF(numberedChildren.childNum = 1, c.Status, NULL)) AS child_entity_1_status
, GROUP_CONCAT(IF(numberedChildren.childNum = 2, c.Name, NULL)) AS child_entity_2_name
, GROUP_CONCAT(IF(numberedChildren.childNum = 2, c.Status, NULL)) AS child_entity_2_status
, ...
FROM (
SELECT #cNum := IF(#prevParent <> orderedChildren.parent_id, #cNum + 1, 1) AS childNum
, orderedChildren.id AS child_id
, #prevParent := orderedChildren.parent_id AS parent_id
FROM (
SELECT parent_id, id
FROM sometable
ORDER BY parent_id, id
) AS orderedChildren
) AS numberedChildren
INNER JOIN sometable AS p ON numberedChildren.parent_id = p.id
INNER JOIN sometable AS c ON numberedChildren.child_id = c.id
GROUP BY p.id, p.Name
;
I think this script might work. It relies on GROUP_CONCAT, and pretty much any other aggregate function, ignoring null values.
You can probably also make it a single query (dropping the initial SET statements) by changing this line:
) AS orderedChildren
to
) AS orderedChildren, (SELECT #cNum AS cnInit, #prevParent AS ppInit) As init
but that is not my usual style for session variable init.
Edit: Also, ordered children may not NEED to be a subquery (you might be able to do the ORDER BY and childNum calculation in the same subquery) but such use of session variables can be...delicate.
This is a bit messy, and there's probably a better means of storing this, especially because this is only manually scalable.
Assuming a table:
CREATE TABLE
parents
(
entity_id INT PRIMARY KEY AUTO_INCREMENT,
parent_entity_id INT,
name VARCHAR(15),
`status` VARCHAR(15)
);
EDITED
With some sample data:
INSERT INTO
`parents`
(entity_id, parent_entity_id, name, `status`)
VALUES
(1, NULL, 'Parent1', 'sfsd'),
(2, 1, 'Child1A', 'sfsd'),
(3, 1, 'Child1B', 'sfsd'),
(4, 1, 'Child1C', 'sfsd'),
(5, NULL, 'Parent2', 'sfsd'),
(6, 5, 'Child2A', 'sfsd'),
(7, 5, 'Child2B', 'sfsd');
You can create a view, temporary table or permanent table (depending on your ultimate goal) that stores the following:
SET #row_number = 0;
SET #parent_id = 0;
SELECT
#row_number:=CASE
WHEN #parent_id = parent_entity_id THEN #row_number + 1
ELSE 1
END AS `child_num`,
entity_id,
#parent_id:= parent_entity_id as parent_entity_id,
name,
`status`
FROM
`parents`
WHERE
`parent_entity_id` IS NOT NULL
ORDER BY
parent_entity_id ASC,
entity_id ASC;
The above would be easier with SQL Server and using PARTITION BY and ROW_NUMBER, but this is a way around it.
Gives us:
Then, you could join that table/view 3 times, adding a second JOIN condition for the child number. This is demoed here, using a derived table due to the restrictions in SQL Fiddle with data modification, which I supposed could be done all 3 times, though you'd have to look into the efficiency and benchmarking.
http://sqlfiddle.com/#!9/5ddef3/3
Ultimately, it gives us:
I have old tables
Items(vendorId-FK, ManufacturerId (IS NOT FK)),
Vendors(PK- VendorId int),
Manufacturer(PK-ManufacturerId int)
Need to transfer data to new DB and it works on transferring data from vendor, but from Manufacturer it transfer only 30000 (where oldManufacturerId is not null), and other 5000 is not transferred. Any ideas what I'm doing wrong?
New Table Manufacturer
( OldManufacturerID int,
newManufacturerId Uniqueidentifier default newid(),
ManufacturerName varchar (100),
)
New Table Items
( ...
ItemDescription,
ManufacturerId uniqueidentifier,
VendorId uniqueidentifier
)
INSERT INTO dbo.Item
( ...
ItemDescription,
ManufacturerId ,
VendorId
)
SELECT
...
itemDescription,
m.ManufacturerId ,
v.VendorId
FROM OldSqlDatabase.dbo.tbl_Items i
JOIN NewSqlDatabase.dbo.Vendor v ON ISNULL(i.vendor_id, '') = ISNULL(v.SourceVendorID, '')
JOIN NewSqlDatabase.dbo.Manufacturer m ON ISNULL(i.manufacturer_id, '') = ISNULL(m.SourceManufacturerID, '')
I just transfered a data since this table didn't have any relationships, new manufactureId has default newid(), and this new id i want to use in the new item table
INSERT INTO dbo.Manufacturer
( OldManufacturerID ,
ManufacturerName ,
)
SELECT
manufacturer_id ,
manufacturer_name ,
FROM oldManufacture
You likely need to use a LEFT JOIN the get records where there a no values (null values) on which to join. The LEFT JOIN will allow you to select all records from the first table being specified in the join, even if there are no matching records available in the second table in the join.
This would also allow you to get rid of that ISNULL() stuff in the join definitions as well. Those would perform very poorly, as they would not be able to use indexes.
I would suggest something like:
INSERT INTO dbo.Item
( ...
ItemDescription,
ManufacturerId ,
VendorId
)
SELECT
...
itemDescription,
m.ManufacturerId ,
v.VendorId
FROM OldSqlDatabase.dbo.tbl_Items i
LEFT JOIN NewSqlDatabase.dbo.Vendor v
ON i.vendor_id = v.SourceVendorID
LEFT JOIN NewSqlDatabase.dbo.Manufacturer m
ON i.manufacturer_id = m.SourceManufacturerID
First, I have created a table called Placemarks containing a column of type 'geography'.
CREATE TABLE [dbo].[Placemarks](
[ID] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[Location] [geography] NOT NULL,
CONSTRAINT [PK_Placemarks]
PRIMARY KEY CLUSTERED([ID] ASC)
)
Then, I use the following query in a stored procedure to get a list of all columns in the table with their data types.
SELECT
b.name, c.name as TypeName, b.length, b.isnullable, b.collation, b.xprec, b.xscale
FROM sysobjects a
inner join syscolumns b on a.id = b.id
inner join systypes c on b.xtype = c.xtype and c.name <> 'sysname'
WHERE a.id = object_id(N'[dbo].[Placemarks]')
and OBJECTPROPERTY(a.id, N'IsUserTable') = 1
ORDER BY b.colId
The result of the query can be viewed here:
I am using this query in a stored procedure and need to get a single row for each column in my Placemarks table. I could filter out rows with TypeName = geometry or hierarchyid.
But I may use the geometry datatype in the future and want the query to be forward compatible. Any other ideas?
The additional rows are being brought in by the join on systypes. Changing the join condition to
inner join systypes c on b.xtype = c.xtype and b.xusertype=c.xusertype
seems to work. You should use sys.columns, sys.types etc. instead of the deprecated syscolumns, systypes backward compatibility views.
I would recommend using the newer sys system catalog views rather than the old sysobjects and similar views - those will be removed soon.
With this query, you should get your desired result:
SELECT
c.name 'ColName',
ty.Name 'TypeName',
c.max_length, c.is_nullable, c.collation_name, c.precision, c.scale
FROM
sys.tables t
INNER JOIN
sys.columns c ON t.object_id = c.object_id
INNER JOIN
sys.types ty ON c.user_type_id = ty.user_type_id
WHERE
t.name = 'Placemarks'
At least in my case, I now get:
ColName TypeName max_length is_nullable collation_name precision scale
ID int 4 0 NULL 10 0
Name nvarchar 100 0 Latin1_General_CI_AS 0 0
Location geography -1 0 NULL 0 0
Background
I have a table of comments. Each comment belongs to a specific row in the 'lead' table or the 'prod' table. There can be multiple comments for each row in the 'lead' and 'prod' tables. There can also be multiple comments with the same 'kstatus' for each row.
comments
INT id /* primary key */
ENUM('lead', 'prod') type /* tells me whether the comment belongs to a row in the lead table or the prod table */
INT fid /* foreign key, refers to the id column in either the lead or prod table */
MEDIUMTEXT comment /* the comment itself */
INT timestamp /* when the comment was written, i.e. 1300530201 */
INT kstatus /* foreign key, refers to the id column in the kstatus column */
lead
INT id
...
prod
INT id
...
kstatus
INT id
...
A comment with type = 'lead' and fid = 105 refers to the row in lead with lead.id = 105.
A comment with type = 'prod' and fid = 105 refers to the row in prod with prod.id = 105.
Question
I want to SELECT the first comment given (if any, and based on timestamp) for each row in the 'lead' table with a certain kstatus (say 14).
Here is my attempt, which returns way too few results. Maybe due to mixing of fid's referring to lead.id and fid's referring to prod.id?
SELECT C1.*
FROM comments AS C1
LEFT JOIN comments AS C2 ON (C1.timestamp > C2.timestamp AND C1.fid = C2.fid)
WHERE C2.timestamp is NULL
AND C1.type = 'lead'
AND C1.kstatus = 14
GROUP BY C1.fid
select c2.*
from (
select min(c.id) realfirstcomment
from (
select fid, min(timestamp) as firstcomment
from comments
where type='lead' and kstatus=14
group by fid
) f, comments c
where f.fid = c.fid
and c.type='lead' and c.kstatus=14
and f.firstcomment = c.timestamp
group by c.fid
) f2, c2
where f2.realfirstcomment = c2.id
You are not only losing results because of mixing between products and leads, but between kstatus values as well -- your left join finds all rows in C2 with a earlier timestamp, regardless of type and kstatus, as long as the fid is the same.
One solution would be to add AND C1.type = C2.type AND C1.kstatus = C2.kstatus to the join condition.